diff options
347 files changed, 17063 insertions, 14996 deletions
diff --git a/service/Android.bp b/service/Android.bp index 091023e6fe..b5b316f5fd 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -1,4 +1,5 @@ -// Copyright (C) 2011 The Android Open Source Project +// +// Copyright (C) 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,11 +12,32 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +java_defaults { + name: "WifiStackCommon", + min_sdk_version: "29", + platform_apis: true, + privileged: true, + dex_preopt: { + enabled: false, + }, + errorprone: { + javacflags: ["-Xep:CheckReturnValue:ERROR"], + }, + product_variables: { + pdk: { + enabled: false, + }, + }, + resource_dirs: [ + "res", + ], +} // Make the JNI part // ============================================================ cc_library_shared { - name: "libwifi-service", + name: "libwifi-jni", cflags: [ "-Wall", @@ -48,3 +70,102 @@ cc_library_shared { }, }, } + +// Build the wifi-service static library +// ============================================================ +java_library { + name: "wifi-service", + installable: true, + defaults: ["WifiStackCommon"], + srcs: [ + "java/**/*.java", + "java/**/*.logtags", + ":framework-wifistack-shared-srcs", + ], + + libs: [ + "error_prone_annotations", + "libprotobuf-java-lite", + "jsr305", + "services", + ], + + static_libs: [ + "android.hardware.wifi-V1.0-java", + "android.hardware.wifi-V1.1-java", + "android.hardware.wifi-V1.2-java", + "android.hardware.wifi-V1.3-java", + "android.hardware.wifi-V1.4-java", + "android.hardware.wifi.hostapd-V1.0-java", + "android.hardware.wifi.hostapd-V1.1-java", + "android.hardware.wifi.supplicant-V1.0-java", + "android.hardware.wifi.supplicant-V1.1-java", + "android.hardware.wifi.supplicant-V1.2-java", + "android.hardware.wifi.supplicant-V1.3-java", + "android.hidl.manager-V1.2-java", + "androidx.annotation_annotation", + "bouncycastle-unbundled", + "wifi_service_proto", + "ksoap2", + "libnanohttpd", + "services.core", + "services.net", + "services.wifi", + "libwificond_aidl-java", + ], +} + +filegroup { + name: "wifi-jarjar-rules", + srcs: [ "jarjar-rules-shared.txt"] +} + +java_defaults { + name: "WifiStackAppCommon", + defaults: ["WifiStackCommon"], + static_libs: [ + "wifi-service", + ], + jni_libs: [ + "libwifi-jni", + ], + // Resources already included in wifi-service + resource_dirs: [], + jarjar_rules: ":wifi-jarjar-rules", + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + + required: [ + "libwifi-jni", + "services", + "cacerts_wfa", + ], +} + +// Updatable stack packaged as an application +android_app { + name: "WifiStack", + defaults: ["WifiStackAppCommon"], + certificate: "networkstack", + manifest: "AndroidManifest.xml", + use_embedded_native_libs: true, + // The permission configuration *must* be included to ensure security of the device + required: ["WifiStackPermissionConfig"], + init_rc: ["wifi.rc"], +} + +// Non-updatable wifi stack running in the system server process for devices not using the module +// TODO (b/135753701): Test this path. +android_app { + name: "InProcessWifiStack", + defaults: ["WifiStackAppCommon"], + certificate: "platform", + manifest: "AndroidManifest_InProcess.xml", + // InProcessWifiStack is a replacement for WifiStack + overrides: ["WifiStack"], + // The permission configuration *must* be included to ensure security of the device + required: ["PlatformWifiStackPermissionConfig"], + init_rc: ["wifi_inprocess.rc"], +} + diff --git a/service/Android.mk b/service/Android.mk deleted file mode 100644 index b396214c71..0000000000 --- a/service/Android.mk +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -ifneq ($(TARGET_BUILD_PDK), true) - -# 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 $(wificond_aidl_path) -LOCAL_SRC_FILES := $(call all-java-files-under, java) \ - $(call all-Iaidl-files-under, java) \ - $(call all-Iaidl-files-under, $(wificond_aidl_rel_path)) \ - $(call all-logtags-files-under, java) - -LOCAL_JAVA_LIBRARIES := \ - android.hidl.manager-V1.2-java \ - error_prone_annotations \ - libprotobuf-java-lite \ - jsr305 \ - services -LOCAL_STATIC_JAVA_LIBRARIES := \ - android.hardware.wifi-V1.0-java \ - android.hardware.wifi-V1.1-java \ - android.hardware.wifi-V1.2-java \ - android.hardware.wifi-V1.3-java \ - android.hardware.wifi.hostapd-V1.0-java \ - android.hardware.wifi.hostapd-V1.1-java \ - android.hardware.wifi.supplicant-V1.0-java \ - android.hardware.wifi.supplicant-V1.1-java \ - android.hardware.wifi.supplicant-V1.2-java \ - wifi_service_proto \ - ksoap2 \ - libnanohttpd - -LOCAL_REQUIRED_MODULES := \ - services \ - libwifi-service \ - cacerts_wfa -LOCAL_MODULE_TAGS := -LOCAL_MODULE := wifi-service -LOCAL_INIT_RC := wifi-events.rc - -LOCAL_DEX_PREOPT_APP_IMAGE := false -LOCAL_DEX_PREOPT_GENERATE_PROFILE := true -LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING := frameworks/base/services/art-profile -LOCAL_ERROR_PRONE_FLAGS := -Xep:CheckReturnValue:ERROR - -include $(BUILD_JAVA_LIBRARY) - -endif # !TARGET_BUILD_PDK diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml new file mode 100644 index 0000000000..bb0b66e96a --- /dev/null +++ b/service/AndroidManifest.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wifi" + coreApp="true" + android:sharedUserId="android.uid.networkstack" + android:versionCode="220000000" + android:versionName="30 system image"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> + + <!-- Permissions must be defined here, and not in the base manifest, as the wifi stack + running in the system server process does not need any permission, and having privileged + permissions added would cause crashes on startup unless they are also added to the + privileged permissions whitelist for that package. --> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> + <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> + <uses-permission android:name="android.permission.BROADCAST_STICKY" /> + <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> + <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> + <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> + <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> + <uses-permission android:name="android.permission.DEVICE_POWER" /> + <uses-permission android:name="android.permission.DUMP" /> + <uses-permission android:name="android.permission.GET_ACCOUNTS" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" /> + <uses-permission android:name="android.permission.LOCATION_HARDWARE" /> + <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> + <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> + <uses-permission android:name="android.permission.READ_PHONE_STATE" /> + <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> + <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> + <uses-permission android:name="android.permission.REORDER_TASKS" /> + <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" /> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> + <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> + <!-- Signature permission defined for wifi module--> + <uses-permission android:name="android.permission.MAINLINE_WIFI_STACK" /> + + <application + android:persistent="true" + android:directBootAware="true" + android:process="com.android.networkstack.process" + android:label="@string/wifiAppLabel"> + <service + android:name="com.android.server.wifi.WifiStackService" + android:directBootAware="true"> + <intent-filter> + <action android:name="android.net.wifi.IWifiStackConnector"/> + </intent-filter> + </service> + <receiver android:name="com.android.server.wifi.BootCompleteReceiver"> + <intent-filter> + <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> + </intent-filter> + </receiver> + </application> +</manifest> diff --git a/service/AndroidManifest_InProcess.xml b/service/AndroidManifest_InProcess.xml new file mode 100644 index 0000000000..7d242d928d --- /dev/null +++ b/service/AndroidManifest_InProcess.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wifi.inprocess" + coreApp="true" + android:sharedUserId="android.uid.system" + android:versionCode="220000000" + android:versionName="30 system image"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> + + <application + android:persistent="true" + android:directBootAware="true" + android:process="system" + android:label="@string/wifiAppLabel"> + <service + android:name="com.android.server.wifi.WifiStackService" + android:directBootAware="true"> + <intent-filter> + <action android:name="android.net.wifi.IWifiStackConnector.InProcess"/> + </intent-filter> + </service> + <receiver android:name="com.android.server.wifi.BootCompleteReceiver"> + <intent-filter> + <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> + </intent-filter> + </receiver> + </application> +</manifest> diff --git a/service/CleanSpec.mk b/service/CleanSpec.mk index 70e8e55e10..4566bd4f9a 100644 --- a/service/CleanSpec.mk +++ b/service/CleanSpec.mk @@ -1,4 +1,5 @@ -# Copyright (C) 2011 The Android Open Source Project +# -*- mode: makefile -*- +# Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,7 +13,7 @@ # 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 @@ -20,7 +21,7 @@ # # E.g.: # $(call add-clean-step, touch -c external/sqlite/sqlite3.h) -# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# $(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. @@ -37,9 +38,16 @@ # ************************************************ # 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*) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/wifi-service_intermediates) - +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/wifi_inprocess.rc) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessWifiStack) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/service/TEST_MAPPING b/service/TEST_MAPPING new file mode 100644 index 0000000000..faa68964c4 --- /dev/null +++ b/service/TEST_MAPPING @@ -0,0 +1,13 @@ +{ + "presubmit": [ + { + "name": "CtsBackupHostTestCases", + "options": [ + { + "include-filter": "android.cts.backup.OtherSoundsSettingsHostSideTest" + } + ] + } + ] +} + diff --git a/service/jarjar-rules-shared.txt b/service/jarjar-rules-shared.txt new file mode 100644 index 0000000000..8a6435a1a6 --- /dev/null +++ b/service/jarjar-rules-shared.txt @@ -0,0 +1,21 @@ +# We don't jar-jar the entire package because, we still use some classes (like +# AsyncChannel in com.android.internal.util) from these packages which are not +# inside our jar (currently in framework.jar, but will be in wifisdk.jar in the future). +rule com.android.internal.util.FastXmlSerializer* com.android.server.wifi.util.FastXmlSerializer@1 +rule com.android.internal.util.HexDump* com.android.server.wifi.util.HexDump@1 +rule com.android.internal.util.IState* com.android.server.wifi.util.IState@1 +rule com.android.internal.util.MessageUtils* com.android.server.wifi.util.MessageUtils@1 +rule com.android.internal.util.Preconditions* com.android.server.wifi.util.Preconditions@1 +rule com.android.internal.util.State* com.android.server.wifi.util.State@1 +rule com.android.internal.util.StateMachine* com.android.server.wifi.util.StateMachine@1 +rule com.android.internal.util.WakeupMessage* com.android.server.wifi.util.WakeupMessage@1 +rule com.android.internal.util.XmlUtils* com.android.server.wifi.util.XmlUtils@1 + +rule android.util.KeyValueListParser* com.android.server.wifi.util.KeyValueListParser@1 +rule android.util.LocalLog* com.android.server.wifi.util.LocalLog@1 +rule android.util.Rational* com.android.server.wifi.util.Rational@1 +rule android.util.proto.ProtoStream* com.android.server.wifi.util.proto.ProtoStream@1 +rule android.util.proto.ProtoOutputStream* com.android.server.wifi.util.proto.ProtoOutputStream@1 + +# Use our statically linked bouncy castle version +rule org.bouncycastle.** com.android.server.wifi.bouncycastle.@1 diff --git a/service/java/com/android/server/wifi/ActiveModeManager.java b/service/java/com/android/server/wifi/ActiveModeManager.java index 7c50726f9b..0908e26eb2 100644 --- a/service/java/com/android/server/wifi/ActiveModeManager.java +++ b/service/java/com/android/server/wifi/ActiveModeManager.java @@ -30,6 +30,24 @@ import java.lang.annotation.RetentionPolicy; */ public interface ActiveModeManager { /** + * Listener for ActiveModeManager state changes. + */ + interface Listener { + /** + * Invoked when mode manager completes start or on mode switch. + */ + void onStarted(); + /** + * Invoked when mode manager completes stop. + */ + void onStopped(); + /** + * Invoked when mode manager encountered a failure on start or on mode switch. + */ + void onStartFailure(); + } + + /** * Method used to start the Manager for a given Wifi operational mode. */ void start(); diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java index d118d833ad..818c410ae8 100644 --- a/service/java/com/android/server/wifi/ActiveModeWarden.java +++ b/service/java/com/android/server/wifi/ActiveModeWarden.java @@ -17,8 +17,12 @@ package com.android.server.wifi; import android.annotation.NonNull; +import android.content.BroadcastReceiver; import android.content.Context; -import android.net.wifi.WifiConfiguration; +import android.content.Intent; +import android.content.IntentFilter; +import android.location.LocationManager; +import android.net.wifi.WifiClient; import android.net.wifi.WifiManager; import android.os.BatteryStats; import android.os.Handler; @@ -28,78 +32,47 @@ import android.os.RemoteException; import android.util.ArraySet; import android.util.Log; +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; +import com.android.internal.util.IState; +import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import com.android.server.wifi.WifiNative.StatusListener; +import com.android.server.wifi.util.WifiPermissionsUtil; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collection; +import java.util.List; /** * This class provides the implementation for different WiFi operating modes. */ public class ActiveModeWarden { private static final String TAG = "WifiActiveModeWarden"; - - private ModeStateMachine mModeStateMachine; + private static final String STATE_MACHINE_EXITED_STATE_NAME = "STATE_MACHINE_EXITED"; // Holder for active mode managers private final ArraySet<ActiveModeManager> mActiveModeManagers; // DefaultModeManager used to service API calls when there are not active mode managers. - private DefaultModeManager mDefaultModeManager; + private final DefaultModeManager mDefaultModeManager; private final WifiInjector mWifiInjector; - private final Context mContext; private final Looper mLooper; private final Handler mHandler; - private final WifiNative mWifiNative; + private final Context mContext; + private final ClientModeImpl mClientModeImpl; + private final WifiSettingsStore mSettingsStore; + private final FrameworkFacade mFacade; + private final WifiPermissionsUtil mWifiPermissionsUtil; private final IBatteryStats mBatteryStats; - private final SelfRecovery mSelfRecovery; - private BaseWifiDiagnostics mWifiDiagnostics; private final ScanRequestProxy mScanRequestProxy; - - // The base for wifi message types - static final int BASE = Protocol.BASE_WIFI; - - // The message identifiers below are mapped to those in ClientModeImpl when applicable. - // 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; - - // Start Scan Only mode - static final int CMD_START_SCAN_ONLY_MODE = BASE + 200; - // Indicates that start Scan only mode failed - static final int CMD_START_SCAN_ONLY_MODE_FAILURE = BASE + 201; - // Indicates that scan only mode stopped - static final int CMD_STOP_SCAN_ONLY_MODE = BASE + 202; - // ScanOnly mode teardown is complete - static final int CMD_SCAN_ONLY_MODE_STOPPED = BASE + 203; - // ScanOnly mode failed - static final int CMD_SCAN_ONLY_MODE_FAILED = BASE + 204; - - // Start Client mode - static final int CMD_START_CLIENT_MODE = BASE + 300; - // Indicates that start client mode failed - static final int CMD_START_CLIENT_MODE_FAILURE = BASE + 301; - // Indicates that client mode stopped - static final int CMD_STOP_CLIENT_MODE = BASE + 302; - // Client mode teardown is complete - static final int CMD_CLIENT_MODE_STOPPED = BASE + 303; - // Client mode failed - static final int CMD_CLIENT_MODE_FAILED = BASE + 304; - - private StatusListener mWifiNativeStatusListener; + private final WifiController mWifiController; private WifiManager.SoftApCallback mSoftApCallback; - private ScanOnlyModeManager.Listener mScanOnlyCallback; - private ClientModeManager.Listener mClientModeCallback; + private WifiManager.SoftApCallback mLohsCallback; /** * Called from WifiServiceImpl to register a callback for notifications from SoftApManager @@ -109,58 +82,135 @@ public class ActiveModeWarden { } /** - * Called from WifiController to register a callback for notifications from ScanOnlyModeManager - */ - public void registerScanOnlyCallback(@NonNull ScanOnlyModeManager.Listener callback) { - mScanOnlyCallback = callback; - } - - /** - * Called from WifiController to register a callback for notifications from ClientModeManager + * Called from WifiServiceImpl to register a callback for notifications from SoftApManager + * for local-only hotspot. */ - public void registerClientModeCallback(@NonNull ClientModeManager.Listener callback) { - mClientModeCallback = callback; + public void registerLohsCallback(@NonNull WifiManager.SoftApCallback callback) { + mLohsCallback = callback; } ActiveModeWarden(WifiInjector wifiInjector, - Context context, Looper looper, WifiNative wifiNative, DefaultModeManager defaultModeManager, - IBatteryStats batteryStats) { + IBatteryStats batteryStats, + BaseWifiDiagnostics wifiDiagnostics, + Context context, + ClientModeImpl clientModeImpl, + WifiSettingsStore settingsStore, + FrameworkFacade facade, + WifiPermissionsUtil wifiPermissionsUtil) { mWifiInjector = wifiInjector; - mContext = context; mLooper = looper; mHandler = new Handler(looper); - mWifiNative = wifiNative; - mActiveModeManagers = new ArraySet(); + mContext = context; + mClientModeImpl = clientModeImpl; + mSettingsStore = settingsStore; + mFacade = facade; + mWifiPermissionsUtil = wifiPermissionsUtil; + mActiveModeManagers = new ArraySet<>(); mDefaultModeManager = defaultModeManager; mBatteryStats = batteryStats; - mSelfRecovery = mWifiInjector.getSelfRecovery(); - mWifiDiagnostics = mWifiInjector.getWifiDiagnostics(); - mScanRequestProxy = mWifiInjector.getScanRequestProxy(); - mModeStateMachine = new ModeStateMachine(); - mWifiNativeStatusListener = new WifiNativeStatusListener(); - mWifiNative.registerStatusListener(mWifiNativeStatusListener); + mScanRequestProxy = wifiInjector.getScanRequestProxy(); + mWifiController = new WifiController(); + + wifiNative.registerStatusListener(isReady -> { + if (!isReady) { + mHandler.post(() -> { + Log.e(TAG, "One of the native daemons died. Triggering recovery"); + wifiDiagnostics.captureBugReportData( + WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE); + + // immediately trigger SelfRecovery if we receive a notice about an + // underlying daemon failure + // Note: SelfRecovery has a circular dependency with ActiveModeWarden and is + // instantiated after ActiveModeWarden, so use WifiInjector to get the instance + // instead of directly passing in SelfRecovery in the constructor. + mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); + }); + } + }); + } + + /** Begin listening to broadcasts and start the internal state machine. */ + public void start() { + mWifiController.start(); + } + + /** Disable Wifi for recovery purposes. */ + public void recoveryDisableWifi() { + mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); } /** - * Method to switch wifi into client mode where connections to configured networks will be - * attempted. + * Restart Wifi for recovery purposes. + * @param reason One of {@link SelfRecovery.RecoveryReason} */ - public void enterClientMode() { - changeMode(ModeStateMachine.CMD_START_CLIENT_MODE); + public void recoveryRestartWifi(@SelfRecovery.RecoveryReason int reason) { + mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason); + } + + /** Wifi has been toggled. */ + public void wifiToggled() { + mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED); + } + + /** Airplane Mode has been toggled. */ + public void airplaneModeToggled() { + mWifiController.sendMessage(WifiController.CMD_AIRPLANE_TOGGLED); + } + + /** Starts SoftAp. */ + public void startSoftAp(SoftApModeConfiguration softApConfig) { + mWifiController.sendMessage(WifiController.CMD_SET_AP, 1, 0, softApConfig); + } + + /** Stop SoftAp. */ + public void stopSoftAp(int mode) { + mWifiController.sendMessage(WifiController.CMD_SET_AP, 0, mode); + } + + /** Emergency Callback Mode has changed. */ + public void emergencyCallbackModeChanged(boolean isInEmergencyCallbackMode) { + mWifiController.sendMessage( + WifiController.CMD_EMERGENCY_MODE_CHANGED, isInEmergencyCallbackMode ? 1 : 0); + } + + /** Emergency Call state has changed. */ + public void emergencyCallStateChanged(boolean isInEmergencyCall) { + mWifiController.sendMessage( + WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED, isInEmergencyCall ? 1 : 0); + } + + /** Scan always mode has changed. */ + public void scanAlwaysModeChanged() { + mWifiController.sendMessage(WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED); + } + + private boolean hasAnyModeManager() { + return !mActiveModeManagers.isEmpty(); + } + + private boolean hasAnyClientModeManager() { + for (ActiveModeManager manager : mActiveModeManagers) { + if (manager instanceof ClientModeManager) return true; + } + return false; } /** - * 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. + * @return true if all client mode managers are in scan mode, + * false if there are no client mode managers present or if any of them are not in scan mode. */ - public void enterScanOnlyMode() { - changeMode(ModeStateMachine.CMD_START_SCAN_ONLY_MODE); + private boolean areAllClientModeManagersInScanMode() { + boolean hasAnyClientModeManager = false; + for (ActiveModeManager manager : mActiveModeManagers) { + if (!(manager instanceof ClientModeManager)) continue; + ClientModeManager clientModeManager = (ClientModeManager) manager; + hasAnyClientModeManager = true; + if (!clientModeManager.isInScanOnlyMode()) return false; + } + return hasAnyClientModeManager; } /** @@ -168,91 +218,155 @@ public class ActiveModeWarden { * * The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if * the persisted config is to be used) and the target operating mode (ex, - * {@link WifiManager.IFACE_IP_MODE_TETHERED} {@link WifiManager.IFACE_IP_MODE_LOCAL_ONLY}). + * {@link WifiManager#IFACE_IP_MODE_TETHERED} {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). * - * @param wifiConfig SoftApModeConfiguration for the hostapd softap + * @param softApConfig SoftApModeConfiguration for the hostapd softap */ - public void enterSoftAPMode(@NonNull SoftApModeConfiguration wifiConfig) { - mHandler.post(() -> { - startSoftAp(wifiConfig); - }); + private void startSoftApModeManager(@NonNull SoftApModeConfiguration softApConfig) { + Log.d(TAG, "Starting SoftApModeManager config = " + + softApConfig.getWifiConfiguration()); + + SoftApCallbackImpl callback = new SoftApCallbackImpl(softApConfig.getTargetMode()); + SoftApListener listener = new SoftApListener(); + ActiveModeManager manager = + mWifiInjector.makeSoftApManager(listener, callback, softApConfig); + listener.setActiveModeManager(manager); + manager.start(); + mActiveModeManagers.add(manager); } /** - * Method to stop soft ap for wifi hotspot. + * Method to stop all soft ap for the specified mode. * * This method will stop any active softAp mode managers. * * @param mode the operating mode of APs to bring down (ex, - * {@link WifiManager.IFACE_IP_MODE_TETHERED} or - * {@link WifiManager.IFACE_IP_MODE_LOCAL_ONLY}). - * Use {@link WifiManager.IFACE_IP_MODE_UNSPECIFIED} to stop all APs. + * {@link WifiManager#IFACE_IP_MODE_TETHERED} or + * {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY}). + * Use {@link WifiManager#IFACE_IP_MODE_UNSPECIFIED} to stop all APs. */ - public void stopSoftAPMode(int mode) { - mHandler.post(() -> { - for (ActiveModeManager manager : mActiveModeManagers) { - if (!(manager instanceof SoftApManager)) continue; - SoftApManager softApManager = (SoftApManager) manager; - - if (mode != WifiManager.IFACE_IP_MODE_UNSPECIFIED - && mode != softApManager.getIpMode()) { - continue; - } - softApManager.stop(); + private void stopSoftApModeManagers(int mode) { + Log.d(TAG, "Shutting down all softap mode managers in mode " + mode); + for (ActiveModeManager manager : mActiveModeManagers) { + if (!(manager instanceof SoftApManager)) continue; + SoftApManager softApManager = (SoftApManager) manager; + + if (mode != WifiManager.IFACE_IP_MODE_UNSPECIFIED + && mode != softApManager.getIpMode()) { + continue; } - updateBatteryStatsWifiState(false); - }); + softApManager.stop(); + } } /** - * Method to disable wifi in sta/client mode scenarios. - * - * This mode will stop any client/scan modes and will not perform any network scans. + * Method to enable a new client mode manager. */ - public void disableWifi() { - changeMode(ModeStateMachine.CMD_DISABLE_WIFI); + private boolean startClientModeManager() { + Log.d(TAG, "Starting ClientModeManager"); + ClientListener listener = new ClientListener(); + ClientModeManager manager = mWifiInjector.makeClientModeManager(listener); + listener.setActiveModeManager(manager); + manager.start(); + if (!switchClientMode(manager)) { + return false; + } + mActiveModeManagers.add(manager); + return true; } /** - * Method to stop all active modes, for example, when toggling airplane mode. + * Method to stop all client mode mangers. */ - public void shutdownWifi() { - mHandler.post(() -> { - for (ActiveModeManager manager : mActiveModeManagers) { - manager.stop(); + private void stopAllClientModeManagers() { + Log.d(TAG, "Shutting down all client mode managers"); + for (ActiveModeManager manager : mActiveModeManagers) { + if (!(manager instanceof ClientModeManager)) continue; + ClientModeManager clientModeManager = (ClientModeManager) manager; + clientModeManager.stop(); + } + } + + /** + * Method to switch all client mode manager mode of operation (from ScanOnly To Connect & + * vice-versa) based on the toggle state. + */ + private boolean switchAllClientModeManagers() { + Log.d(TAG, "Switching all client mode managers"); + for (ActiveModeManager manager : mActiveModeManagers) { + if (!(manager instanceof ClientModeManager)) continue; + ClientModeManager clientModeManager = (ClientModeManager) manager; + if (!switchClientMode(clientModeManager)) { + return false; } - updateBatteryStatsWifiState(false); - }); + } + updateBatteryStats(); + return true; + } + + /** + * Method to switch a client mode manager mode of operation (from ScanOnly To Connect & + * vice-versa) based on the toggle state. + */ + private boolean switchClientMode(@NonNull ClientModeManager modeManager) { + if (mSettingsStore.isWifiToggleEnabled()) { + modeManager.switchToConnectMode(); + } else if (checkScanOnlyModeAvailable()) { + modeManager.switchToScanOnlyMode(); + } else { + Log.e(TAG, "Something is wrong, no client mode toggles enabled"); + return false; + } + return true; + } + + /** + * Method to stop all active modes, for example, when toggling airplane mode. + */ + private void shutdownWifi() { + Log.d(TAG, "Shutting down all mode managers"); + for (ActiveModeManager manager : mActiveModeManagers) { + manager.stop(); + } } /** * Dump current state for active mode managers. * - * Must be called from ClientModeImpl thread. + * Must be called from the main Wifi thread. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Dump of " + TAG); - pw.println("Current wifi mode: " + getCurrentMode()); pw.println("NumActiveModeManagers: " + mActiveModeManagers.size()); for (ActiveModeManager manager : mActiveModeManagers) { manager.dump(fd, pw, args); } + mWifiController.dump(fd, pw, args); } - protected String getCurrentMode() { - return mModeStateMachine.getCurrentMode(); + @VisibleForTesting + String getCurrentMode() { + IState state = mWifiController.getCurrentState(); + return state == null ? STATE_MACHINE_EXITED_STATE_NAME : state.getName(); } - private void changeMode(int newMode) { - mModeStateMachine.sendMessage(newMode); + @VisibleForTesting + Collection<ActiveModeManager> getActiveModeManagers() { + return new ArraySet<>(mActiveModeManagers); + } + + @VisibleForTesting + boolean isInEmergencyMode() { + IState state = mWifiController.getCurrentState(); + return ((WifiController.BaseState) state).isInEmergencyMode(); } /** * Helper class to wrap the ActiveModeManager callback objects. */ - private class ModeCallback { - ActiveModeManager mActiveManager; + private static class ModeCallback { + private ActiveModeManager mActiveManager; void setActiveModeManager(ActiveModeManager manager) { mActiveManager = manager; @@ -263,289 +377,500 @@ public class ActiveModeWarden { } } - private class ModeStateMachine extends StateMachine { - // Commands for the state machine - these will be removed, - // along with the StateMachine itself - public static final int CMD_START_CLIENT_MODE = 0; - public static final int CMD_START_SCAN_ONLY_MODE = 1; - public static final int CMD_DISABLE_WIFI = 3; + private class SoftApCallbackImpl implements WifiManager.SoftApCallback { + private final int mMode; - private final State mWifiDisabledState = new WifiDisabledState(); - private final State mClientModeActiveState = new ClientModeActiveState(); - private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState(); + SoftApCallbackImpl(int mode) { + Preconditions.checkArgument(mode == WifiManager.IFACE_IP_MODE_TETHERED + || mode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + mMode = mode; + } - ModeStateMachine() { - super(TAG, mLooper); + @Override + public void onStateChanged(int state, int reason) { + switch (mMode) { + case WifiManager.IFACE_IP_MODE_TETHERED: + if (mSoftApCallback != null) mSoftApCallback.onStateChanged(state, reason); + break; + case WifiManager.IFACE_IP_MODE_LOCAL_ONLY: + if (mLohsCallback != null) mLohsCallback.onStateChanged(state, reason); + break; + } + } - addState(mClientModeActiveState); - addState(mScanOnlyModeActiveState); - addState(mWifiDisabledState); + @Override + public void onConnectedClientsChanged(List<WifiClient> clients) { + switch (mMode) { + case WifiManager.IFACE_IP_MODE_TETHERED: + if (mSoftApCallback != null) { + mSoftApCallback.onConnectedClientsChanged(clients); + } + break; + case WifiManager.IFACE_IP_MODE_LOCAL_ONLY: + if (mLohsCallback != null) { + mLohsCallback.onConnectedClientsChanged(clients); + } + break; + } + } + } - Log.d(TAG, "Starting Wifi in WifiDisabledState"); - setInitialState(mWifiDisabledState); - start(); + private void updateBatteryStats() { + updateBatteryStatsWifiState(hasAnyModeManager()); + if (areAllClientModeManagersInScanMode()) { + updateBatteryStatsScanModeActive(); } + } - private String getCurrentMode() { - return getCurrentState().getName(); + private class SoftApListener extends ModeCallback implements ActiveModeManager.Listener { + @Override + public void onStarted() { + updateBatteryStats(); } - private boolean checkForAndHandleModeChange(Message message) { - switch(message.what) { - case ModeStateMachine.CMD_START_CLIENT_MODE: - Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode"); - mModeStateMachine.transitionTo(mClientModeActiveState); + @Override + public void onStopped() { + mActiveModeManagers.remove(getActiveModeManager()); + updateBatteryStats(); + mWifiController.sendMessage(WifiController.CMD_AP_STOPPED); + } + + @Override + public void onStartFailure() { + mActiveModeManagers.remove(getActiveModeManager()); + updateBatteryStats(); + mWifiController.sendMessage(WifiController.CMD_AP_START_FAILURE); + } + } + + private class ClientListener extends ModeCallback implements ActiveModeManager.Listener { + @Override + public void onStarted() { + updateScanMode(); + updateBatteryStats(); + } + + @Override + public void onStopped() { + mActiveModeManagers.remove(getActiveModeManager()); + updateScanMode(); + updateBatteryStats(); + mWifiController.sendMessage(WifiController.CMD_STA_STOPPED); + } + + @Override + public void onStartFailure() { + mActiveModeManagers.remove(getActiveModeManager()); + updateScanMode(); + updateBatteryStats(); + mWifiController.sendMessage(WifiController.CMD_STA_START_FAILURE); + } + } + + // Update the scan state based on all active mode managers. + private void updateScanMode() { + boolean scanEnabled = false; + boolean scanningForHiddenNetworksEnabled = false; + for (ActiveModeManager modeManager : mActiveModeManagers) { + @ActiveModeManager.ScanMode int scanState = modeManager.getScanMode(); + switch (scanState) { + case ActiveModeManager.SCAN_NONE: break; - case ModeStateMachine.CMD_START_SCAN_ONLY_MODE: - Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode"); - mModeStateMachine.transitionTo(mScanOnlyModeActiveState); + case ActiveModeManager.SCAN_WITHOUT_HIDDEN_NETWORKS: + scanEnabled = true; break; - case ModeStateMachine.CMD_DISABLE_WIFI: - Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled"); - mModeStateMachine.transitionTo(mWifiDisabledState); + case ActiveModeManager.SCAN_WITH_HIDDEN_NETWORKS: + scanEnabled = true; + scanningForHiddenNetworksEnabled = true; break; - default: - return NOT_HANDLED; } - return HANDLED; } + mScanRequestProxy.enableScanning(scanEnabled, scanningForHiddenNetworksEnabled); + } - class ModeActiveState extends State { - ActiveModeManager mManager; - @Override - public boolean processMessage(Message message) { - // handle messages for changing modes here - return NOT_HANDLED; - } - - @Override - public void exit() { - // Active states must have a mode manager, so this should not be null, but it isn't - // obvious from the structure - add a null check here, just in case this is missed - // in the future - if (mManager != null) { - mManager.stop(); - mActiveModeManagers.remove(mManager); - updateScanMode(); + /** + * Helper method to report wifi state as on/off (doesn't matter which mode). + * + * @param enabled boolean indicating that some mode has been turned on or off + */ + private void updateBatteryStatsWifiState(boolean enabled) { + try { + if (enabled) { + if (mActiveModeManagers.size() == 1) { + // only report wifi on if we haven't already + mBatteryStats.noteWifiOn(); + } + } else { + if (mActiveModeManagers.size() == 0) { + // only report if we don't have any active modes + mBatteryStats.noteWifiOff(); } - updateBatteryStatsWifiState(false); } + } catch (RemoteException e) { + Log.e(TAG, "Failed to note battery stats in wifi"); + } + } - // Hook to be used by sub-classes of ModeActiveState to indicate the completion of - // bringup of the corresponding mode. - public void onModeActivationComplete() { - updateScanMode(); + private void updateBatteryStatsScanModeActive() { + try { + mBatteryStats.noteWifiState(BatteryStats.WIFI_STATE_OFF_SCANNING, null); + } catch (RemoteException e) { + Log.e(TAG, "Failed to note battery stats in wifi"); + } + } + + private boolean checkScanOnlyModeAvailable() { + return mWifiPermissionsUtil.isLocationModeEnabled() + && mSettingsStore.isScanAlwaysAvailable(); + } + + /** + * WifiController is the class used to manage wifi state for various operating + * modes (normal, airplane, wifi hotspot, etc.). + */ + private class WifiController extends StateMachine { + private static final String TAG = "WifiController"; + + // Maximum limit to use for timeout delay if the value from overlay setting is too large. + private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000; + + private final int mRecoveryDelayMillis; + + private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; + + static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; + static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; + static final int CMD_WIFI_TOGGLED = BASE + 8; + static final int CMD_AIRPLANE_TOGGLED = BASE + 9; + static final int CMD_SET_AP = BASE + 10; + static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; + static final int CMD_AP_STOPPED = BASE + 15; + static final int CMD_STA_START_FAILURE = BASE + 16; + // Command used to trigger a wifi stack restart when in active mode + static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; + // Internal command used to complete wifi stack restart + private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; + // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full + // recovery + static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; + static final int CMD_STA_STOPPED = BASE + 20; + static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI = BASE + 22; + static final int CMD_AP_START_FAILURE = BASE + 23; + + private final EnabledState mEnabledState = new EnabledState(); + private final DisabledState mDisabledState = new DisabledState(); + + private boolean mIsInEmergencyCall = false; + private boolean mIsInEmergencyCallbackMode = false; + + WifiController() { + super(TAG, mLooper); + + DefaultState defaultState = new DefaultState(); + addState(defaultState); { + addState(mDisabledState, defaultState); + addState(mEnabledState, defaultState); } - // Update the scan state based on all active mode managers. - // Note: This is an overkill currently because there is only 1 of scan-only or client - // mode present today. - private void updateScanMode() { - boolean scanEnabled = false; - boolean scanningForHiddenNetworksEnabled = false; - for (ActiveModeManager modeManager : mActiveModeManagers) { - @ActiveModeManager.ScanMode int scanState = modeManager.getScanMode(); - switch (scanState) { - case ActiveModeManager.SCAN_NONE: - break; - case ActiveModeManager.SCAN_WITHOUT_HIDDEN_NETWORKS: - scanEnabled = true; - break; - case ActiveModeManager.SCAN_WITH_HIDDEN_NETWORKS: - scanEnabled = true; - scanningForHiddenNetworksEnabled = true; - break; - } + setLogRecSize(100); + setLogOnlyTransitions(false); + + mRecoveryDelayMillis = readWifiRecoveryDelay(); + } + + @Override + public void start() { + boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); + boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); + boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); + boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled(); + + log("isAirplaneModeOn = " + isAirplaneModeOn + + ", isWifiEnabled = " + isWifiEnabled + + ", isScanningAvailable = " + isScanningAlwaysAvailable + + ", isLocationModeActive = " + isLocationModeActive); + + if (shouldEnableSta()) { + startClientModeManager(); + setInitialState(mEnabledState); + } else { + setInitialState(mDisabledState); + } + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Location mode has been toggled... trigger with the scan change + // update to make sure we are in the correct mode + scanAlwaysModeChanged(); } - mScanRequestProxy.enableScanning(scanEnabled, scanningForHiddenNetworksEnabled); + }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); + super.start(); + } + + private int readWifiRecoveryDelay() { + int recoveryDelayMillis = mContext.getResources().getInteger( + R.integer.config_wifi_framework_recovery_timeout_delay); + if (recoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) { + recoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS; + Log.w(TAG, "Overriding timeout delay with maximum limit value"); } + return recoveryDelayMillis; } - class WifiDisabledState extends ModeActiveState { - @Override - public void enter() { - Log.d(TAG, "Entering WifiDisabledState"); + abstract class BaseState extends State { + @VisibleForTesting + boolean isInEmergencyMode() { + return mIsInEmergencyCall || mIsInEmergencyCallbackMode; } - @Override - public boolean processMessage(Message message) { - Log.d(TAG, "received a message in WifiDisabledState: " + message); - if (checkForAndHandleModeChange(message)) { - return HANDLED; + private void updateEmergencyMode(Message msg) { + if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED) { + mIsInEmergencyCall = msg.arg1 == 1; + } else if (msg.what == CMD_EMERGENCY_MODE_CHANGED) { + mIsInEmergencyCallbackMode = msg.arg1 == 1; } - return NOT_HANDLED; } - @Override - public void exit() { - // do not have an active mode manager... nothing to clean up + private void enterEmergencyMode() { + stopSoftApModeManagers(WifiManager.IFACE_IP_MODE_UNSPECIFIED); + boolean configWiFiDisableInECBM = mFacade.getConfigWiFiDisableInECBM(mContext); + log("WifiController msg getConfigWiFiDisableInECBM " + configWiFiDisableInECBM); + if (configWiFiDisableInECBM) { + shutdownWifi(); + } } - } + private void exitEmergencyMode() { + if (shouldEnableSta()) { + startClientModeManager(); + transitionTo(mEnabledState); + } else { + transitionTo(mDisabledState); + } + } - class ClientModeActiveState extends ModeActiveState { - ClientListener mListener; - private class ClientListener implements ClientModeManager.Listener { - @Override - public void onStateChanged(int state) { - // make sure this listener is still active - if (this != mListener) { - Log.d(TAG, "Client mode state change from previous manager"); - return; + @Override + public final boolean processMessage(Message msg) { + // potentially enter emergency mode + if (msg.what == CMD_EMERGENCY_CALL_STATE_CHANGED + || msg.what == CMD_EMERGENCY_MODE_CHANGED) { + boolean wasInEmergencyMode = isInEmergencyMode(); + updateEmergencyMode(msg); + boolean isInEmergencyMode = isInEmergencyMode(); + if (!wasInEmergencyMode && isInEmergencyMode) { + enterEmergencyMode(); + } else if (wasInEmergencyMode && !isInEmergencyMode) { + exitEmergencyMode(); } - - Log.d(TAG, "State changed from client mode. state = " + state); - - if (state == WifiManager.WIFI_STATE_UNKNOWN) { - // error while setting up client mode or an unexpected failure. - mModeStateMachine.sendMessage(CMD_CLIENT_MODE_FAILED, this); - } else if (state == WifiManager.WIFI_STATE_DISABLED) { - // client mode stopped - mModeStateMachine.sendMessage(CMD_CLIENT_MODE_STOPPED, this); - } else if (state == WifiManager.WIFI_STATE_ENABLED) { - // client mode is ready to go - Log.d(TAG, "client mode active"); - onModeActivationComplete(); - } else { - // only care if client mode stopped or started, dropping + return HANDLED; + } else if (isInEmergencyMode()) { + // already in emergency mode, drop all messages other than mode stop messages + // triggered by emergency mode start. + if (msg.what == CMD_STA_STOPPED || msg.what == CMD_AP_STOPPED) { + if (!hasAnyModeManager()) { + log("No active mode managers, return to DisabledState."); + transitionTo(mDisabledState); + } } + return HANDLED; } + // not in emergency mode, process messages normally + return processMessageFiltered(msg); } + protected abstract boolean processMessageFiltered(Message msg); + } + + class DefaultState extends State { @Override - public void enter() { - Log.d(TAG, "Entering ClientModeActiveState"); + public boolean processMessage(Message msg) { + switch (msg.what) { + case CMD_SCAN_ALWAYS_MODE_CHANGED: + case CMD_WIFI_TOGGLED: + case CMD_STA_STOPPED: + case CMD_STA_START_FAILURE: + case CMD_AP_STOPPED: + case CMD_AP_START_FAILURE: + case CMD_RECOVERY_RESTART_WIFI_CONTINUE: + case CMD_DEFERRED_RECOVERY_RESTART_WIFI: + break; + case CMD_RECOVERY_DISABLE_WIFI: + log("Recovery has been throttled, disable wifi"); + shutdownWifi(); + // onStopped will move the state machine to "DisabledState". + break; + case CMD_AIRPLANE_TOGGLED: + if (mSettingsStore.isAirplaneModeOn()) { + log("Airplane mode toggled, shutdown all modes"); + shutdownWifi(); + // onStopped will move the state machine to "DisabledState". + } else { + log("Airplane mode disabled, determine next state"); + if (shouldEnableSta()) { + startClientModeManager(); + transitionTo(mEnabledState); + } + // wifi should remain disabled, do not need to transition + } + break; + default: + throw new RuntimeException("WifiController.handleMessage " + msg.what); + } + return HANDLED; + } + } - mListener = new ClientListener(); - mManager = mWifiInjector.makeClientModeManager(mListener); - mManager.start(); - mActiveModeManagers.add(mManager); + private boolean shouldEnableSta() { + return mSettingsStore.isWifiToggleEnabled() || checkScanOnlyModeAvailable(); + } - updateBatteryStatsWifiState(true); + class DisabledState extends BaseState { + @Override + public void enter() { + log("DisabledState.enter()"); + super.enter(); + if (hasAnyModeManager()) { + Log.e(TAG, "Entered DisabledState, but has active mode managers"); + } } @Override public void exit() { + log("DisabledState.exit()"); super.exit(); - mListener = null; } @Override - public boolean processMessage(Message message) { - if (checkForAndHandleModeChange(message)) { - return HANDLED; - } - - switch(message.what) { - case CMD_START_CLIENT_MODE: - Log.d(TAG, "Received CMD_START_CLIENT_MODE when active - drop"); + public boolean processMessageFiltered(Message msg) { + switch (msg.what) { + case CMD_WIFI_TOGGLED: + case CMD_SCAN_ALWAYS_MODE_CHANGED: + if (shouldEnableSta()) { + startClientModeManager(); + transitionTo(mEnabledState); + } break; - case CMD_CLIENT_MODE_FAILED: - if (mListener != message.obj) { - Log.d(TAG, "Client mode state change from previous manager"); - return HANDLED; + case CMD_SET_AP: + // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here + if (msg.arg1 == 1) { + startSoftApModeManager((SoftApModeConfiguration) msg.obj); + transitionTo(mEnabledState); } - Log.d(TAG, "ClientMode failed, return to WifiDisabledState."); - // notify WifiController that ClientMode failed - mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN); - mModeStateMachine.transitionTo(mWifiDisabledState); break; - case CMD_CLIENT_MODE_STOPPED: - if (mListener != message.obj) { - Log.d(TAG, "Client mode state change from previous manager"); - return HANDLED; + case CMD_RECOVERY_RESTART_WIFI: + log("Recovery triggered, already in disabled state"); + // intentional fallthrough + case CMD_DEFERRED_RECOVERY_RESTART_WIFI: + // wait mRecoveryDelayMillis for letting driver clean reset. + sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, + mRecoveryDelayMillis); + break; + case CMD_RECOVERY_RESTART_WIFI_CONTINUE: + if (shouldEnableSta()) { + startClientModeManager(); + transitionTo(mEnabledState); } - - Log.d(TAG, "ClientMode stopped, return to WifiDisabledState."); - // notify WifiController that ClientMode stopped - mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED); - mModeStateMachine.transitionTo(mWifiDisabledState); break; default: return NOT_HANDLED; } - return NOT_HANDLED; + return HANDLED; } } - class ScanOnlyModeActiveState extends ModeActiveState { - ScanOnlyListener mListener; - private class ScanOnlyListener implements ScanOnlyModeManager.Listener { - @Override - public void onStateChanged(int state) { - if (this != mListener) { - Log.d(TAG, "ScanOnly mode state change from previous manager"); - return; - } - - if (state == WifiManager.WIFI_STATE_UNKNOWN) { - Log.d(TAG, "ScanOnlyMode mode failed"); - // error while setting up scan mode or an unexpected failure. - mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_FAILED, this); - } else if (state == WifiManager.WIFI_STATE_DISABLED) { - Log.d(TAG, "ScanOnlyMode stopped"); - //scan only mode stopped - mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_STOPPED, this); - } else if (state == WifiManager.WIFI_STATE_ENABLED) { - // scan mode is ready to go - Log.d(TAG, "scan mode active"); - onModeActivationComplete(); - } else { - Log.d(TAG, "unexpected state update: " + state); - } - } - } - + class EnabledState extends BaseState { @Override public void enter() { - Log.d(TAG, "Entering ScanOnlyModeActiveState"); - - mListener = new ScanOnlyListener(); - mManager = mWifiInjector.makeScanOnlyModeManager(mListener); - mManager.start(); - mActiveModeManagers.add(mManager); - - updateBatteryStatsWifiState(true); - updateBatteryStatsScanModeActive(); + log("EnabledState.enter()"); + super.enter(); + if (!hasAnyModeManager()) { + Log.e(TAG, "Entered EnabledState, but no active mode managers"); + } } @Override public void exit() { + log("EnabledState.exit()"); + if (hasAnyModeManager()) { + Log.e(TAG, "Existing EnabledState, but has active mode managers"); + } super.exit(); - mListener = null; } @Override - public boolean processMessage(Message message) { - if (checkForAndHandleModeChange(message)) { - return HANDLED; - } - - switch(message.what) { - case CMD_START_SCAN_ONLY_MODE: - Log.d(TAG, "Received CMD_START_SCAN_ONLY_MODE when active - drop"); + public boolean processMessageFiltered(Message msg) { + switch (msg.what) { + case CMD_WIFI_TOGGLED: + case CMD_SCAN_ALWAYS_MODE_CHANGED: + if (shouldEnableSta()) { + if (hasAnyClientModeManager()) { + switchAllClientModeManagers(); + } else { + startClientModeManager(); + } + } else { + stopAllClientModeManagers(); + } break; - case CMD_SCAN_ONLY_MODE_FAILED: - if (mListener != message.obj) { - Log.d(TAG, "ScanOnly mode state change from previous manager"); - return HANDLED; + case CMD_SET_AP: + // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here + if (msg.arg1 == 1) { + startSoftApModeManager((SoftApModeConfiguration) msg.obj); + } else { + stopSoftApModeManagers(msg.arg2); } - - Log.d(TAG, "ScanOnlyMode failed, return to WifiDisabledState."); - // notify WifiController that ScanOnlyMode failed - mScanOnlyCallback.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN); - mModeStateMachine.transitionTo(mWifiDisabledState); break; - case CMD_SCAN_ONLY_MODE_STOPPED: - if (mListener != message.obj) { - Log.d(TAG, "ScanOnly mode state change from previous manager"); + case CMD_AIRPLANE_TOGGLED: + // airplane mode toggled on is handled in the default state + if (mSettingsStore.isAirplaneModeOn()) { + return NOT_HANDLED; + } else { + // when airplane mode is toggled off, but wifi is on, we can keep it on + log("airplane mode toggled - and airplane mode is off. return handled"); return HANDLED; } - - Log.d(TAG, "ScanOnlyMode stopped, return to WifiDisabledState."); - // notify WifiController that ScanOnlyMode stopped - mScanOnlyCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED); - mModeStateMachine.transitionTo(mWifiDisabledState); + case CMD_AP_STOPPED: + case CMD_AP_START_FAILURE: + if (!hasAnyModeManager()) { + if (shouldEnableSta()) { + log("SoftAp disabled, start client mode"); + startClientModeManager(); + } else { + log("SoftAp mode disabled, return to DisabledState"); + transitionTo(mDisabledState); + } + } else { + log("AP disabled, remain in EnabledState."); + } + break; + case CMD_STA_START_FAILURE: + case CMD_STA_STOPPED: + // Client mode stopped. Head to Disabled to wait for next command if there + // no active mode managers. + if (!hasAnyModeManager()) { + log("STA disabled, return to DisabledState."); + transitionTo(mDisabledState); + } else { + log("STA disabled, remain in EnabledState."); + } + break; + case CMD_RECOVERY_RESTART_WIFI: + final String bugTitle; + final String bugDetail; + if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) { + bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1]; + bugTitle = "Wi-Fi BugReport: " + bugDetail; + } else { + bugDetail = ""; + bugTitle = "Wi-Fi BugReport"; + } + if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) { + mHandler.post(() -> mClientModeImpl.takeBugReport(bugTitle, bugDetail)); + } + log("Recovery triggered, disable wifi"); + deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI)); + shutdownWifi(); + // onStopped will move the state machine to "DisabledState". break; default: return NOT_HANDLED; @@ -553,105 +878,5 @@ public class ActiveModeWarden { return HANDLED; } } - } // class ModeStateMachine - - private class SoftApCallbackImpl extends ModeCallback implements WifiManager.SoftApCallback { - private int mMode; - - private SoftApCallbackImpl(int mode) { - mMode = mode; - } - - @Override - public void onStateChanged(int state, int reason) { - if (state == WifiManager.WIFI_AP_STATE_DISABLED) { - mActiveModeManagers.remove(getActiveModeManager()); - updateBatteryStatsWifiState(false); - } else if (state == WifiManager.WIFI_AP_STATE_FAILED) { - mActiveModeManagers.remove(getActiveModeManager()); - updateBatteryStatsWifiState(false); - } - - if (mSoftApCallback != null && mMode == WifiManager.IFACE_IP_MODE_TETHERED) { - mSoftApCallback.onStateChanged(state, reason); - } - } - - @Override - public void onNumClientsChanged(int numClients) { - if (mSoftApCallback == null) { - Log.d(TAG, "SoftApCallback is null. Dropping NumClientsChanged event."); - } else if (mMode == WifiManager.IFACE_IP_MODE_TETHERED) { - mSoftApCallback.onNumClientsChanged(numClients); - } - } } - - private void startSoftAp(SoftApModeConfiguration softapConfig) { - Log.d(TAG, "Starting SoftApModeManager"); - - WifiConfiguration config = softapConfig.getWifiConfiguration(); - if (config != null && config.SSID != null) { - Log.d(TAG, "Passing config to SoftApManager! " + config); - } else { - config = null; - } - - SoftApCallbackImpl callback = new SoftApCallbackImpl(softapConfig.getTargetMode()); - ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig); - callback.setActiveModeManager(manager); - manager.start(); - mActiveModeManagers.add(manager); - updateBatteryStatsWifiState(true); - } - - /** - * Helper method to report wifi state as on/off (doesn't matter which mode). - * - * @param enabled boolean indicating that some mode has been turned on or off - */ - private void updateBatteryStatsWifiState(boolean enabled) { - try { - if (enabled) { - if (mActiveModeManagers.size() == 1) { - // only report wifi on if we haven't already - mBatteryStats.noteWifiOn(); - } - } else { - if (mActiveModeManagers.size() == 0) { - // only report if we don't have any active modes - mBatteryStats.noteWifiOff(); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Failed to note battery stats in wifi"); - } - } - - private void updateBatteryStatsScanModeActive() { - try { - mBatteryStats.noteWifiState(BatteryStats.WIFI_STATE_OFF_SCANNING, null); - } catch (RemoteException e) { - Log.e(TAG, "Failed to note battery stats in wifi"); - } - } - - // callback used to receive callbacks about underlying native failures - private final class WifiNativeStatusListener implements StatusListener { - - @Override - public void onStatusChanged(boolean isReady) { - if (!isReady) { - mHandler.post(() -> { - Log.e(TAG, "One of the native daemons died. Triggering recovery"); - mWifiDiagnostics.captureBugReportData( - WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE); - - // immediately trigger SelfRecovery if we receive a notice about an - // underlying daemon failure - mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - }); - } - } - }; } diff --git a/service/java/com/android/server/wifi/AvailableNetworkNotifier.java b/service/java/com/android/server/wifi/AvailableNetworkNotifier.java index 7def2f37ae..7af22934f9 100644 --- a/service/java/com/android/server/wifi/AvailableNetworkNotifier.java +++ b/service/java/com/android/server/wifi/AvailableNetworkNotifier.java @@ -31,13 +31,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; +import android.net.wifi.IActionListener; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiManager; +import android.os.Binder; import android.os.Handler; import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; @@ -58,7 +57,7 @@ import java.util.List; import java.util.Set; /** - * Base class for all network notifiers (e.g. OpenNetworkNotifier, CarrierNetworkNotifier). + * Base class for all network notifiers (e.g. OpenNetworkNotifier). * * NOTE: These API's are not thread safe and should only be used from WifiCoreThread. */ @@ -126,7 +125,6 @@ public class AvailableNetworkNotifier { private final Clock mClock; private final WifiConfigManager mConfigManager; private final ClientModeImpl mClientModeImpl; - private final Messenger mSrcMessenger; private final ConnectToNetworkNotificationBuilder mNotificationBuilder; private ScanResult mRecommendedNetwork; @@ -176,7 +174,6 @@ public class AvailableNetworkNotifier { mClientModeImpl = clientModeImpl; mNotificationBuilder = connectToNetworkNotificationBuilder; mScreenOn = false; - mSrcMessenger = new Messenger(new Handler(looper, mConnectionStateCallback)); wifiConfigStore.registerStoreData(new SsidSetStoreData(mStoreDataIdentifier, new AvailableNetworkNotifierStoreData())); @@ -223,21 +220,19 @@ public class AvailableNetworkNotifier { } }; - private final Handler.Callback mConnectionStateCallback = (Message msg) -> { - switch (msg.what) { + private final class ConnectActionListener extends IActionListener.Stub { + @Override + public void onSuccess() { // Success here means that an attempt to connect to the network has been initiated. // Successful connection updates are received via the // WifiConnectivityManager#handleConnectionStateChanged() callback. - case WifiManager.CONNECT_NETWORK_SUCCEEDED: - break; - case WifiManager.CONNECT_NETWORK_FAILED: - handleConnectionAttemptFailedToSend(); - break; - default: - Log.e("AvailableNetworkNotifier", "Unknown message " + msg.what); } - return true; - }; + + @Override + public void onFailure(int reason) { + handleConnectionAttemptFailedToSend(); + } + } /** * Clears the pending notification. This is called by {@link WifiConnectivityManager} on stop. @@ -264,8 +259,9 @@ public class AvailableNetworkNotifier { } private boolean isControllerEnabled() { - return mSettingEnabled && !UserManager.get(mContext) - .hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT); + return mSettingEnabled && !mContext.getSystemService(UserManager.class) + // TODO (b/142234604): This will not work on multi-user device scenarios. + .hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT_OR_SELF); } /** @@ -438,13 +434,9 @@ public class AvailableNetworkNotifier { NetworkUpdateResult result = mConfigManager.addOrUpdateNetwork(network, Process.WIFI_UID); if (result.isSuccess()) { mWifiMetrics.setNominatorForNetwork(result.netId, mNominatorId); - - Message msg = Message.obtain(); - msg.what = WifiManager.CONNECT_NETWORK; - msg.arg1 = result.netId; - msg.obj = null; - msg.replyTo = mSrcMessenger; - mClientModeImpl.sendMessage(msg); + ConnectActionListener connectActionListener = new ConnectActionListener(); + mClientModeImpl.connect(null, result.netId, new Binder(), connectActionListener, + connectActionListener.hashCode(), Process.WIFI_UID); addNetworkToBlacklist(mRecommendedNetwork.SSID); } diff --git a/service/java/com/android/server/wifi/BootCompleteReceiver.java b/service/java/com/android/server/wifi/BootCompleteReceiver.java new file mode 100644 index 0000000000..86f263a2d4 --- /dev/null +++ b/service/java/com/android/server/wifi/BootCompleteReceiver.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.concurrent.GuardedBy; + +/** + * Receives boot complete broadcast (registered in AndroidManifest.xml). + * + * Ensures that if WifiStack is initialized after boot complete, we can check whether boot was + * already completed, since if we start listening for the boot complete broadcast now it will be too + * late and we will never get the broadcast. + * + * This BroadcastReceiver can be registered multiple times in different places, and it will ensure + * that all registered callbacks are triggered exactly once. + */ +public class BootCompleteReceiver extends BroadcastReceiver { + private static final String TAG = "WifiBootCompleteReceiver"; + + private static final Object sLock = new Object(); + @GuardedBy("sLock") + private static boolean sIsBootCompleted = false; + @GuardedBy("sLock") + private static final List<Runnable> sCallbacks = new ArrayList<>(1); + + public BootCompleteReceiver() { + Log.d(TAG, "Constructed BootCompleteReceiver"); + } + + /** + * Registers a callback that will be triggered when boot is completed. Note that if boot has + * already been completed when the callback is registered, the callback will be triggered + * immediately. + * + * No guarantees are made about which thread the callback is triggered on. Please do not + * perform expensive operations in the callback, instead post to other threads. + */ + public static void registerCallback(Runnable callback) { + boolean runImmediately = false; + + synchronized (sLock) { + if (sIsBootCompleted) { + runImmediately = true; + } else { + sCallbacks.add(callback); + } + } + + // run callback outside of synchronized block + if (runImmediately) { + Log.d(TAG, "Triggering callback immediately since boot is already complete."); + callback.run(); + } else { + Log.d(TAG, "Enqueuing callback since boot is not yet complete."); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "Received boot complete broadcast"); + + List<Runnable> callbacks = new ArrayList<>(1); + + synchronized (sLock) { + sIsBootCompleted = true; + callbacks.addAll(sCallbacks); + sCallbacks.clear(); + } + + // run callbacks outside of synchronized block + for (Runnable r : callbacks) { + Log.d(TAG, "Triggered callback"); + r.run(); + } + } +} diff --git a/service/java/com/android/server/wifi/BubbleFunScorer.java b/service/java/com/android/server/wifi/BubbleFunScorer.java index 497c22dce5..b5da5f03e8 100644 --- a/service/java/com/android/server/wifi/BubbleFunScorer.java +++ b/service/java/com/android/server/wifi/BubbleFunScorer.java @@ -33,12 +33,14 @@ final class BubbleFunScorer implements WifiCandidates.CandidateScorer { * This should match WifiNetworkSelector.experimentIdFromIdentifier(getIdentifier()) * when using the default ScoringParams. */ - public static final int BUBBLE_FUN_SCORER_DEFAULT_EXPID = 42598151; + public static final int BUBBLE_FUN_SCORER_DEFAULT_EXPID = 42598152; - private static final double SECURITY_AWARD = 80.0; - private static final double CURRENT_NETWORK_BOOST = 80.0; - private static final double LOW_BAND_FACTOR = 0.3; + private static final double SECURITY_AWARD = 44.0; + private static final double CURRENT_NETWORK_BOOST = 22.0; + private static final double LAST_SELECTION_BOOST = 250.0; + private static final double LOW_BAND_FACTOR = 0.25; private static final double TYPICAL_SCAN_RSSI_STD = 4.0; + private static final boolean USE_USER_CONNECT_CHOICE = true; private final ScoringParams mScoringParams; @@ -48,7 +50,7 @@ final class BubbleFunScorer implements WifiCandidates.CandidateScorer { @Override public String getIdentifier() { - return "BubbleFunScorer_v1"; + return "BubbleFunScorer_v2"; } /** @@ -65,18 +67,6 @@ final class BubbleFunScorer implements WifiCandidates.CandidateScorer { // If we are below the entry threshold, make the score more negative if (score < 0.0) score *= 10.0; - // A recently selected network gets a large boost - score += candidate.getLastSelectionWeight() * CURRENT_NETWORK_BOOST; - - // Hysteresis to prefer staying on the current network. - if (candidate.isCurrentNetwork()) { - score += CURRENT_NETWORK_BOOST; - } - - if (!candidate.isOpenNetwork()) { - score += SECURITY_AWARD; - } - // The gain is approximately the derivative of shapeFunction at the given rssi // This is used to estimate the error double gain = shapeFunction(rssi + 0.5) @@ -89,7 +79,20 @@ final class BubbleFunScorer implements WifiCandidates.CandidateScorer { gain *= LOW_BAND_FACTOR; } - return new ScoredCandidate(score, TYPICAL_SCAN_RSSI_STD * gain, candidate); + // A recently selected network gets a large boost + score += candidate.getLastSelectionWeight() * LAST_SELECTION_BOOST; + + // Hysteresis to prefer staying on the current network. + if (candidate.isCurrentNetwork()) { + score += CURRENT_NETWORK_BOOST; + } + + if (!candidate.isOpenNetwork()) { + score += SECURITY_AWARD; + } + + return new ScoredCandidate(score, TYPICAL_SCAN_RSSI_STD * gain, + USE_USER_CONNECT_CHOICE, candidate); } /** @@ -114,9 +117,9 @@ final class BubbleFunScorer implements WifiCandidates.CandidateScorer { } @Override - public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> group) { + public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> candidates) { ScoredCandidate choice = ScoredCandidate.NONE; - for (Candidate candidate : group) { + for (Candidate candidate : candidates) { ScoredCandidate scoredCandidate = scoreCandidate(candidate); if (scoredCandidate.value > choice.value) { choice = scoredCandidate; @@ -127,9 +130,4 @@ final class BubbleFunScorer implements WifiCandidates.CandidateScorer { return choice; } - @Override - public boolean userConnectChoiceOverrideWanted() { - return true; - } - } diff --git a/service/java/com/android/server/wifi/CarrierNetworkConfig.java b/service/java/com/android/server/wifi/CarrierNetworkConfig.java index 4c92d6b391..174b5d7236 100644 --- a/service/java/com/android/server/wifi/CarrierNetworkConfig.java +++ b/service/java/com/android/server/wifi/CarrierNetworkConfig.java @@ -26,7 +26,6 @@ import android.net.Uri; import android.net.wifi.EAPConstants; import android.net.wifi.WifiEnterpriseConfig; import android.os.Handler; -import android.os.Looper; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.ImsiEncryptionInfo; @@ -69,7 +68,7 @@ public class CarrierNetworkConfig { mDbg = verbose > 0; } - public CarrierNetworkConfig(@NonNull Context context, @NonNull Looper looper, + public CarrierNetworkConfig(@NonNull Context context, @NonNull Handler handler, @NonNull FrameworkFacade framework) { mCarrierNetworkMap = new HashMap<>(); updateNetworkConfig(context); @@ -84,13 +83,17 @@ public class CarrierNetworkConfig { } }, filter); - framework.registerContentObserver(context, CONTENT_URI, false, - new ContentObserver(new Handler(looper)) { - @Override - public void onChange(boolean selfChange) { - updateNetworkConfig(context); - } - }); + try { + framework.registerContentObserver(context, CONTENT_URI, false, + new ContentObserver(handler) { + @Override + public void onChange(boolean selfChange) { + updateNetworkConfig(context); + } + }); + } catch (SecurityException e) { + Log.e(TAG, "Failed to register content observer", e); + } } /** diff --git a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java b/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java index 52d7d18449..105b3c69b1 100644 --- a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java @@ -141,7 +141,8 @@ public class CarrierNetworkEvaluator implements NetworkEvaluator { mLocalLog.log(TAG + ": Failed to add carrier network: " + config); continue; } - if (!mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID)) { + if (!mWifiConfigManager.enableNetwork( + result.getNetworkId(), false, Process.WIFI_UID, null)) { mLocalLog.log(TAG + ": Failed to enable carrier network: " + config); continue; } diff --git a/service/java/com/android/server/wifi/CarrierNetworkNotifier.java b/service/java/com/android/server/wifi/CarrierNetworkNotifier.java deleted file mode 100644 index 5af4001810..0000000000 --- a/service/java/com/android/server/wifi/CarrierNetworkNotifier.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.net.wifi.WifiConfiguration.KeyMgmt; -import android.net.wifi.WifiEnterpriseConfig; -import android.net.wifi.WifiEnterpriseConfig.Eap; -import android.os.Looper; -import android.provider.Settings; - -import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.server.wifi.nano.WifiMetricsProto; - -/** - * This class handles the "carrier wi-fi network available" notification - * - * NOTE: These API's are not thread safe and should only be used from ClientModeImpl thread. - */ -public class CarrierNetworkNotifier extends AvailableNetworkNotifier { - public static final String TAG = "WifiCarrierNetworkNotifier"; - private static final String STORE_DATA_IDENTIFIER = "CarrierNetworkNotifierBlacklist"; - private static final String TOGGLE_SETTINGS_NAME = - Settings.Global.WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON; - - public CarrierNetworkNotifier( - Context context, - Looper looper, - FrameworkFacade framework, - Clock clock, - WifiMetrics wifiMetrics, - WifiConfigManager wifiConfigManager, - WifiConfigStore wifiConfigStore, - ClientModeImpl clientModeImpl, - ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder) { - super(TAG, STORE_DATA_IDENTIFIER, TOGGLE_SETTINGS_NAME, - SystemMessage.NOTE_CARRIER_NETWORK_AVAILABLE, - WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER, - context, looper, framework, clock, - wifiMetrics, wifiConfigManager, wifiConfigStore, clientModeImpl, - connectToNetworkNotificationBuilder); - } - - @Override - WifiConfiguration createRecommendedNetworkConfig(ScanResult recommendedNetwork) { - WifiConfiguration network = super.createRecommendedNetworkConfig(recommendedNetwork); - - int eapMethod = recommendedNetwork.carrierApEapType; - if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) { - network.allowedKeyManagement.set(KeyMgmt.WPA_EAP); - network.allowedKeyManagement.set(KeyMgmt.IEEE8021X); - network.enterpriseConfig = new WifiEnterpriseConfig(); - network.enterpriseConfig.setEapMethod(recommendedNetwork.carrierApEapType); - network.enterpriseConfig.setIdentity(""); - network.enterpriseConfig.setAnonymousIdentity(""); - } - - return network; - } -} diff --git a/service/java/com/android/server/wifi/CellularLinkLayerStats.java b/service/java/com/android/server/wifi/CellularLinkLayerStats.java deleted file mode 100644 index 25f2949a75..0000000000 --- a/service/java/com/android/server/wifi/CellularLinkLayerStats.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.telephony.SignalStrength; -import android.telephony.TelephonyManager; -import android.telephony.TelephonyManager.NetworkType; - -/** - * A class representing the link layer statistics of the primary registered cell - * of cellular network - */ -public class CellularLinkLayerStats { - /** Cellular data network type currently in use on the device for data transmission */ - private @NetworkType int mDataNetworkType = - TelephonyManager.NETWORK_TYPE_UNKNOWN; - /** - * Cellular signal strength in dBm, NR: CsiRsrp, LTE: Rsrp, WCDMA/TDSCDMA: Rscp, - * CDMA: Rssi, EVDO: Rssi, GSM: Rssi - */ - private int mSignalStrengthDbm = SignalStrength.INVALID; - /** - * Cellular signal strength in dB, NR: CsiSinr, LTE: Rsrq, WCDMA: EcNo, TDSCDMA: invalid, - * CDMA: Ecio, EVDO: SNR, GSM: invalid - */ - private int mSignalStrengthDb = SignalStrength.INVALID; - /** Whether it is a new or old registered cell */ - private boolean mIsSameRegisteredCell = false; - - public void setDataNetworkType(@NetworkType int dataNetworkType) { - mDataNetworkType = dataNetworkType; - } - - public void setSignalStrengthDbm(int signalStrengthDbm) { - mSignalStrengthDbm = signalStrengthDbm; - } - - public void setIsSameRegisteredCell(boolean isSameRegisteredCell) { - mIsSameRegisteredCell = isSameRegisteredCell; - } - - public void setSignalStrengthDb(int signalStrengthDb) { - mSignalStrengthDb = signalStrengthDb; - } - - public @NetworkType int getDataNetworkType() { - return mDataNetworkType; - } - - public boolean getIsSameRegisteredCell() { - return mIsSameRegisteredCell; - } - - public int getSignalStrengthDb() { - return mSignalStrengthDb; - } - - public int getSignalStrengthDbm() { - return mSignalStrengthDbm; - } - - @Override - public String toString() { - StringBuilder sbuf = new StringBuilder(); - sbuf.append(" CellularLinkLayerStats: ").append('\n') - .append(" Data Network Type: ") - .append(mDataNetworkType).append('\n') - .append(" Signal Strength in dBm: ") - .append(mSignalStrengthDbm).append('\n') - .append(" Signal Strength in dB: ") - .append(mSignalStrengthDb).append('\n') - .append(" Is it the same registered cell? ") - .append(mIsSameRegisteredCell).append('\n'); - return sbuf.toString(); - } -} diff --git a/service/java/com/android/server/wifi/CellularLinkLayerStatsCollector.java b/service/java/com/android/server/wifi/CellularLinkLayerStatsCollector.java deleted file mode 100644 index 81d219ccec..0000000000 --- a/service/java/com/android/server/wifi/CellularLinkLayerStatsCollector.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.telephony.TelephonyManager.NETWORK_TYPE_CDMA; -import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_0; -import static android.telephony.TelephonyManager.NETWORK_TYPE_GSM; -import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE; -import static android.telephony.TelephonyManager.NETWORK_TYPE_NR; -import static android.telephony.TelephonyManager.NETWORK_TYPE_TD_SCDMA; -import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS; -import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN; - -import android.content.Context; -import android.telephony.CellInfo; -import android.telephony.CellInfoCdma; -import android.telephony.CellInfoGsm; -import android.telephony.CellInfoLte; -import android.telephony.CellInfoNr; -import android.telephony.CellInfoTdscdma; -import android.telephony.CellInfoWcdma; -import android.telephony.CellSignalStrength; -import android.telephony.CellSignalStrengthCdma; -import android.telephony.CellSignalStrengthGsm; -import android.telephony.CellSignalStrengthLte; -import android.telephony.CellSignalStrengthNr; -import android.telephony.CellSignalStrengthTdscdma; -import android.telephony.CellSignalStrengthWcdma; -import android.telephony.SignalStrength; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; -/** - * A class collecting the latest cellular link layer stats - */ -public class CellularLinkLayerStatsCollector { - private static final String TAG = "CellStatsCollector"; - private static final boolean DBG = false; - - private Context mContext; - private SubscriptionManager mSubManager = null; - private TelephonyManager mCachedDefaultDataTelephonyManager = null; - private int mCachedDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private CellInfo mLastPrimaryCellInfo = null; - private int mLastDataNetworkType = NETWORK_TYPE_UNKNOWN; - - public CellularLinkLayerStatsCollector(Context context) { - mContext = context; - } - - /** - * Get the latest DataNetworkType, SignalStrength, CellInfo and other information from - * default data sim's TelephonyManager, parse the values of primary registered cell and return - * them as an instance of CellularLinkLayerStats - */ - public CellularLinkLayerStats update() { - CellularLinkLayerStats cellStats = new CellularLinkLayerStats(); - - retrieveDefaultDataTelephonyManager(); - if (mCachedDefaultDataTelephonyManager == null) { - if (DBG) Log.v(TAG, cellStats.toString()); - return cellStats; - } - - SignalStrength signalStrength = mCachedDefaultDataTelephonyManager.getSignalStrength(); - List<CellSignalStrength> cssList = null; - if (signalStrength != null) cssList = signalStrength.getCellSignalStrengths(); - - if (mCachedDefaultDataTelephonyManager.getDataNetworkType() == NETWORK_TYPE_UNKNOWN - || cssList == null || cssList.size() == 0) { - mLastPrimaryCellInfo = null; - mLastDataNetworkType = NETWORK_TYPE_UNKNOWN; - if (DBG) Log.v(TAG, cellStats.toString()); - return cellStats; - } - if (DBG) Log.v(TAG, "Cell Signal Strength List size = " + cssList.size()); - - CellSignalStrength primaryCss = cssList.get(0); - cellStats.setSignalStrengthDbm(primaryCss.getDbm()); - - updateSignalStrengthDbAndNetworkTypeOfCellStats(primaryCss, cellStats); - - int networkType = cellStats.getDataNetworkType(); - CellInfo primaryCellInfo = getPrimaryCellInfo(mCachedDefaultDataTelephonyManager, - networkType); - boolean isSameRegisteredCell = getIsSameRegisteredCell(primaryCellInfo, networkType); - cellStats.setIsSameRegisteredCell(isSameRegisteredCell); - - // Update for the next call - mLastPrimaryCellInfo = primaryCellInfo; - mLastDataNetworkType = networkType; - - if (DBG) Log.v(TAG, cellStats.toString()); - return cellStats; - } - - private void retrieveDefaultDataTelephonyManager() { - if (!initSubManager()) return; - - int defaultDataSubId = mSubManager.getDefaultDataSubscriptionId(); - if (DBG) Log.v(TAG, "default Data Sub ID = " + defaultDataSubId); - if (defaultDataSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - mCachedDefaultDataTelephonyManager = null; - return; - } - - if (defaultDataSubId != mCachedDefaultDataSubId - || mCachedDefaultDataTelephonyManager == null) { - mCachedDefaultDataSubId = defaultDataSubId; - // TODO(b/132188983): Inject this using WifiInjector - TelephonyManager defaultSubTelephonyManager = (TelephonyManager) mContext - .getSystemService(Context.TELEPHONY_SERVICE); - if (defaultDataSubId == mSubManager.getDefaultSubscriptionId()) { - mCachedDefaultDataTelephonyManager = defaultSubTelephonyManager; - } else { - mCachedDefaultDataTelephonyManager = defaultSubTelephonyManager - .createForSubscriptionId(defaultDataSubId); - } - } - } - - private boolean initSubManager() { - if (mSubManager == null) { - mSubManager = (SubscriptionManager) mContext.getSystemService( - Context.TELEPHONY_SUBSCRIPTION_SERVICE); - } - return (mSubManager != null); - } - - /** - * Update dB value and network type base on CellSignalStrength subclass type. - * It follows the same order as that in SignalStrength.getPrimary(). - * TODO: NR may move up in the future - */ - private void updateSignalStrengthDbAndNetworkTypeOfCellStats(CellSignalStrength primaryCss, - CellularLinkLayerStats cellStats) { - if (primaryCss instanceof CellSignalStrengthLte) { - CellSignalStrengthLte cssLte = (CellSignalStrengthLte) primaryCss; - cellStats.setSignalStrengthDb(cssLte.getRsrq()); - cellStats.setDataNetworkType(NETWORK_TYPE_LTE); - } else if (primaryCss instanceof CellSignalStrengthCdma) { - CellSignalStrengthCdma cssCdma = (CellSignalStrengthCdma) primaryCss; - int evdoSnr = cssCdma.getEvdoSnr(); - int cdmaEcio = cssCdma.getCdmaEcio(); - if (evdoSnr != SignalStrength.INVALID) { - cellStats.setSignalStrengthDb(evdoSnr); - cellStats.setDataNetworkType(NETWORK_TYPE_EVDO_0); - } else { - cellStats.setSignalStrengthDb(cdmaEcio); - cellStats.setDataNetworkType(NETWORK_TYPE_CDMA); - } - } else if (primaryCss instanceof CellSignalStrengthTdscdma) { - cellStats.setDataNetworkType(NETWORK_TYPE_TD_SCDMA); - } else if (primaryCss instanceof CellSignalStrengthWcdma) { - CellSignalStrengthWcdma cssWcdma = (CellSignalStrengthWcdma) primaryCss; - cellStats.setSignalStrengthDb(cssWcdma.getEcNo()); - cellStats.setDataNetworkType(NETWORK_TYPE_UMTS); - } else if (primaryCss instanceof CellSignalStrengthGsm) { - cellStats.setDataNetworkType(NETWORK_TYPE_GSM); - } else if (primaryCss instanceof CellSignalStrengthNr) { - CellSignalStrengthNr cssNr = (CellSignalStrengthNr) primaryCss; - cellStats.setSignalStrengthDb(cssNr.getCsiSinr()); - cellStats.setDataNetworkType(NETWORK_TYPE_NR); - } else { - Log.e(TAG, "invalid CellSignalStrength"); - } - } - - private CellInfo getPrimaryCellInfo(TelephonyManager defaultDataTelephonyManager, - int networkType) { - List<CellInfo> cellInfoList = getRegisteredCellInfo(defaultDataTelephonyManager); - int cilSize = cellInfoList.size(); - CellInfo primaryCellInfo = null; - // CellInfo.getCellConnectionStatus() should tell if it is primary serving cell. - // However, it currently always returns 0 (CONNECTION_NONE) for registered cells. - // Therefore, the workaround of deriving primary serving cell is - // to check if the registered cellInfo subclass type matches networkType - for (int i = 0; i < cilSize; ++i) { - CellInfo cellInfo = cellInfoList.get(i); - if ((cellInfo instanceof CellInfoTdscdma && networkType == NETWORK_TYPE_TD_SCDMA) - || (cellInfo instanceof CellInfoCdma && (networkType == NETWORK_TYPE_CDMA - || networkType == NETWORK_TYPE_EVDO_0)) - || (cellInfo instanceof CellInfoLte && networkType == NETWORK_TYPE_LTE) - || (cellInfo instanceof CellInfoWcdma && networkType == NETWORK_TYPE_UMTS) - || (cellInfo instanceof CellInfoGsm && networkType == NETWORK_TYPE_GSM) - || (cellInfo instanceof CellInfoNr && networkType == NETWORK_TYPE_NR)) { - primaryCellInfo = cellInfo; - } - } - return primaryCellInfo; - } - - private boolean getIsSameRegisteredCell(CellInfo primaryCellInfo, int networkType) { - boolean isSameRegisteredCell; - if (primaryCellInfo != null && mLastPrimaryCellInfo != null) { - isSameRegisteredCell = primaryCellInfo.getCellIdentity() - .equals(mLastPrimaryCellInfo.getCellIdentity()); - } else if (primaryCellInfo == null && mLastPrimaryCellInfo == null) { - // This is a workaround when it can't find primaryCellInfo for two consecutive times. - isSameRegisteredCell = true; - } else { - // only one of them is null and it is a strong indication of primary cell change. - isSameRegisteredCell = false; - } - - if (mLastDataNetworkType == NETWORK_TYPE_UNKNOWN || mLastDataNetworkType != networkType) { - isSameRegisteredCell = false; - } - return isSameRegisteredCell; - } - - private List<CellInfo> getRegisteredCellInfo(TelephonyManager defaultDataTelephonyManager) { - List<CellInfo> allList = defaultDataTelephonyManager.getAllCellInfo(); - List<CellInfo> cellInfoList = new ArrayList<>(); - for (CellInfo ci : allList) { - if (ci.isRegistered()) cellInfoList.add(ci); - if (DBG) Log.v(TAG, ci.toString()); - } - return cellInfoList; - } -} diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 438f88550e..2d14f4e7d4 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -16,6 +16,8 @@ package com.android.server.wifi; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; @@ -30,7 +32,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.database.ContentObserver; import android.net.ConnectivityManager; @@ -57,8 +58,9 @@ import android.net.ip.IIpClient; import android.net.ip.IpClientCallbacks; import android.net.ip.IpClientManager; import android.net.shared.ProvisioningConfiguration; +import android.net.wifi.IActionListener; import android.net.wifi.INetworkRequestMatchCallback; -import android.net.wifi.RssiPacketCountInfo; +import android.net.wifi.ITxPacketCountListener; import android.net.wifi.ScanResult; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; @@ -70,7 +72,6 @@ import android.net.wifi.WifiNetworkAgentSpecifier; import android.net.wifi.WifiSsid; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; -import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.p2p.IWifiP2pManager; import android.os.BatteryStats; import android.os.Bundle; @@ -114,6 +115,7 @@ import com.android.server.wifi.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; import com.android.server.wifi.nano.WifiMetricsProto.WifiUsabilityStats; import com.android.server.wifi.p2p.WifiP2pServiceImpl; +import com.android.server.wifi.util.ExternalCallbackTracker; import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.TelephonyUtil; import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData; @@ -132,9 +134,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -218,6 +218,7 @@ public class ClientModeImpl extends StateMachine { private final LinkProbeManager mLinkProbeManager; private final McastLockManagerFilterController mMcastLockManagerFilterController; + private final ActivityManager mActivityManager; private boolean mScreenOn = false; @@ -256,7 +257,7 @@ public class ClientModeImpl extends StateMachine { } private boolean mEnableRssiPolling = false; - // Accessed via Binder thread ({get,set}PollRssiIntervalMsecs), and ClientModeImpl thread. + // Accessed via Binder thread ({get,set}PollRssiIntervalMsecs), and the main Wifi thread. private volatile int mPollRssiIntervalMsecs = DEFAULT_POLL_RSSI_INTERVAL_MSECS; private int mRssiPollToken = 0; /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE @@ -422,24 +423,16 @@ public class ClientModeImpl extends StateMachine { // Provide packet filter capabilities to ConnectivityService. private final NetworkMisc mNetworkMisc = new NetworkMisc(); + private final ExternalCallbackTracker<IActionListener> mProcessingActionListeners; + private final ExternalCallbackTracker<ITxPacketCountListener> mProcessingTxPacketCountListeners; + /* The base for wifi message types */ static final int BASE = Protocol.BASE_WIFI; static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31; - /* Supplicant commands */ - /* 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; - /* Get configured networks */ - static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59; /* 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; /* Supplicant commands after driver start*/ @@ -491,43 +484,16 @@ public class ClientModeImpl extends StateMachine { /* Disconnecting state watchdog */ static final int CMD_DISCONNECTING_WATCHDOG_TIMER = BASE + 96; - /* Remove a packages associated configurations */ - static final int CMD_REMOVE_APP_CONFIGURATIONS = BASE + 97; - - /* Disable an ephemeral network */ - static final int CMD_DISABLE_EPHEMERAL_NETWORK = BASE + 98; - /* SIM is removed; reset any cached data for it */ static final int CMD_RESET_SIM_NETWORKS = BASE + 101; /* OSU APIs */ 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; - - // Add or update a Passpoint configuration. - static final int CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG = BASE + 106; - - // 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; - - // Get the list of OSU providers associated with a Passpoint network. - static final int CMD_GET_MATCHING_OSU_PROVIDERS = BASE + 109; - - // Get the list of installed Passpoint configurations matched with OSU providers - static final int CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS = BASE + 110; - /* Commands from/to the SupplicantStateTracker */ /* Reset the supplicant state tracker */ static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111; - // Get the list of wifi configurations for installed Passpoint profiles - static final int CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES = BASE + 112; - int mDisconnectingWatchdogCount = 0; static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000; @@ -549,9 +515,6 @@ public class ClientModeImpl extends StateMachine { /* Link configuration (IP address, DNS, ...) changes notified via netlink */ static final int CMD_UPDATE_LINKPROPERTIES = BASE + 140; - /* Supplicant is trying to associate to a given BSSID */ - static final int CMD_TARGET_BSSID = BASE + 141; - static final int CMD_START_CONNECT = BASE + 143; private static final int NETWORK_STATUS_UNWANTED_DISCONNECT = 0; @@ -562,16 +525,11 @@ public class ClientModeImpl extends StateMachine { static final int CMD_START_ROAM = BASE + 145; - static final int CMD_ASSOCIATED_BSSID = BASE + 147; - static final int CMD_NETWORK_STATUS = BASE + 148; /* A layer 3 neighbor on the Wi-Fi link became unreachable. */ static final int CMD_IP_REACHABILITY_LOST = BASE + 149; - /* Remove a packages associated configrations */ - static final int CMD_REMOVE_USER_CONFIGURATIONS = BASE + 152; - static final int CMD_ACCEPT_UNVALIDATED = BASE + 153; /* used to offload sending IP packet */ @@ -592,10 +550,6 @@ public class ClientModeImpl extends StateMachine { /* Enable/Disable WifiConnectivityManager */ static final int CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER = BASE + 166; - - /* Get FQDN list for Passpoint profiles matched with a given scanResults */ - static final int CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS = BASE + 168; - /** * Used to handle messages bounced between ClientModeImpl and IpClient. */ @@ -611,15 +565,6 @@ public class ClientModeImpl extends StateMachine { /* 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; - /* Read the APF program & data buffer */ static final int CMD_READ_PACKET_FILTER = BASE + 208; @@ -640,6 +585,10 @@ public class ClientModeImpl extends StateMachine { private static final int CMD_PRE_DHCP_ACTION_COMPLETE = BASE + 256; private static final int CMD_POST_DHCP_ACTION = BASE + 257; + private static final int CMD_CONNECT_NETWORK = BASE + 258; + private static final int CMD_SAVE_NETWORK = BASE + 259; + private static final int CMD_PKT_CNT_FETCH = BASE + 261; + // For message logging. private static final Class[] sMessageClasses = { AsyncChannel.class, ClientModeImpl.class }; @@ -820,6 +769,7 @@ public class ClientModeImpl extends StateMachine { mLinkProperties = new LinkProperties(); mMcastLockManagerFilterController = new McastLockManagerFilterController(); + mActivityManager = context.getSystemService(ActivityManager.class); mNetworkInfo.setIsAvailable(false); mLastBssid = null; @@ -851,6 +801,8 @@ public class ClientModeImpl extends StateMachine { mNetworkCapabilitiesFilter, mWifiConnectivityManager); mWifiNetworkSuggestionsManager = mWifiInjector.getWifiNetworkSuggestionsManager(); + mProcessingActionListeners = new ExternalCallbackTracker<>(getHandler()); + mProcessingTxPacketCountListeners = new ExternalCallbackTracker<>(getHandler()); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); @@ -895,6 +847,7 @@ public class ClientModeImpl extends StateMachine { mTcpBufferSizes = mContext.getResources().getString( R.string.config_wifi_tcp_buffers); + mWifiConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener()); // CHECKSTYLE:OFF IndentationCheck addState(mDefaultState); @@ -925,8 +878,9 @@ public class ClientModeImpl extends StateMachine { } private void registerForWifiMonitorEvents() { - mWifiMonitor.registerHandler(mInterfaceName, CMD_TARGET_BSSID, getHandler()); - mWifiMonitor.registerHandler(mInterfaceName, CMD_ASSOCIATED_BSSID, getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.TARGET_BSSID_EVENT, getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATED_BSSID_EVENT, + getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ANQP_DONE_EVENT, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATION_REJECTION_EVENT, getHandler()); @@ -960,12 +914,18 @@ public class ClientModeImpl extends StateMachine { mWifiMetrics.getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, mWifiMetrics.getHandler()); - mWifiMonitor.registerHandler(mInterfaceName, CMD_ASSOCIATED_BSSID, + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATED_BSSID_EVENT, mWifiMetrics.getHandler()); - mWifiMonitor.registerHandler(mInterfaceName, CMD_TARGET_BSSID, + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.TARGET_BSSID_EVENT, mWifiMetrics.getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT, mWifiInjector.getWifiLastResortWatchdog().getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATION_REJECTION_EVENT, + mSupplicantStateTracker.getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.AUTHENTICATION_FAILURE_EVENT, + mSupplicantStateTracker.getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, + mSupplicantStateTracker.getHandler()); } private void setMulticastFilter(boolean enabled) { @@ -1089,6 +1049,62 @@ public class ClientModeImpl extends StateMachine { } /** + * Listener for config manager network config related events. + * TODO (b/117601161) : Move some of the existing handling in WifiConnectivityManager's listener + * for the same events. + */ + private class OnNetworkUpdateListener implements + WifiConfigManager.OnNetworkUpdateListener { + @Override + public void onNetworkAdded(WifiConfiguration config) { } + + @Override + public void onNetworkEnabled(WifiConfiguration config) { } + + @Override + public void onNetworkRemoved(WifiConfiguration config) { + // The current connected or connecting network has been removed, trigger a disconnect. + if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { + // Disconnect and let autojoin reselect a new network + sendMessage(CMD_DISCONNECT); + } + mWifiNative.removeNetworkCachedData(config.networkId); + } + + + @Override + public void onNetworkUpdated(WifiConfiguration config) { + // User might have changed meteredOverride, so update capabilities + if (config.networkId == mLastNetworkId) { + updateCapabilities(); + } + } + + @Override + public void onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason) { + if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return; + if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { + // Disconnect and let autojoin reselect a new network + sendMessage(CMD_DISCONNECT); + } + + } + + @Override + public void onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason) { + // For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network + // because supplicant won't be trying to reconnect. If this is due to a + // preventAutomaticReconnect request from ConnectivityService, that service + // will disconnect as appropriate. + if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return; + if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { + // Disconnect and let autojoin reselect a new network + sendMessage(CMD_DISCONNECT); + } + } + } + + /** * Set wpa_supplicant log level using |mVerboseLoggingLevel| flag. */ void setSupplicantLogLevel() { @@ -1103,7 +1119,7 @@ public class ClientModeImpl extends StateMachine { public void enableVerboseLogging(int verbose) { if (verbose > 0) { mVerboseLoggingEnabled = true; - setLogRecSize(ActivityManager.isLowRamDeviceStatic() + setLogRecSize(mActivityManager.isLowRamDevice() ? NUM_LOG_RECS_VERBOSE_LOW_MEMORY : NUM_LOG_RECS_VERBOSE); } else { mVerboseLoggingEnabled = false; @@ -1158,23 +1174,9 @@ public class ClientModeImpl extends StateMachine { * @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) { + private void connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) { logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid + ", forceReconnect = " + forceReconnect); - WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId); - if (config == null) { - loge("connectToUserSelectNetwork Invalid network Id=" + netId); - return false; - } - if (!mWifiConfigManager.enableNetwork(netId, true, uid) - || !mWifiConfigManager.updateLastConnectUid(netId, uid)) { - logi("connectToUserSelectNetwork Allowing uid " + uid - + " with insufficient permissions to connect=" + netId); - } else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { - // 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. @@ -1182,12 +1184,11 @@ public class ClientModeImpl extends StateMachine { } else { mWifiConnectivityManager.prepareForForcedConnection(netId); if (uid == Process.SYSTEM_UID) { - mWifiMetrics.setNominatorForNetwork(config.networkId, + mWifiMetrics.setNominatorForNetwork(netId, WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL); } startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY); } - return true; } /** @@ -1539,20 +1540,6 @@ public class ClientModeImpl extends StateMachine { } /** - * Blocking method to match the provider with the current network - * - * @param channel AsyncChannel to use for the response - * @param fqdn - * @return int returns message result - */ - public int matchProviderWithCurrentNetwork(AsyncChannel channel, String fqdn) { - Message resultMsg = channel.sendMessageSynchronously(CMD_MATCH_PROVIDER_NETWORK, fqdn); - int result = resultMsg.arg1; - resultMsg.recycle(); - return result; - } - - /** * Deauthenticate and set the re-authentication hold off time for the current network * @param holdoff hold off time in milliseconds * @param ess set if the hold off pertains to an ESS rather than a BSS @@ -1562,17 +1549,6 @@ public class ClientModeImpl extends StateMachine { } /** - * Method to disable an ephemeral config for an ssid - * - * @param ssid network name to disable - */ - public void disableEphemeralNetwork(String ssid) { - if (ssid != null) { - sendMessage(CMD_DISABLE_EPHEMERAL_NETWORK, ssid); - } - } - - /** * Disconnect from Access Point */ public void disconnectCommand() { @@ -1621,197 +1597,6 @@ public class ClientModeImpl extends StateMachine { private AtomicInteger mNullMessageCounter = new AtomicInteger(0); /** - * Add a network synchronously - * - * @return network id of the new network - */ - public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) { - Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config); - if (messageIsNull(resultMsg)) return WifiConfiguration.INVALID_NETWORK_ID; - int result = resultMsg.arg1; - resultMsg.recycle(); - return result; - } - - /** - * Get configured networks synchronously - * - * @param channel - * @return - */ - public List<WifiConfiguration> syncGetConfiguredNetworks(int uuid, AsyncChannel channel, - int targetUid) { - Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid, - targetUid); - if (messageIsNull(resultMsg)) return null; - List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj; - resultMsg.recycle(); - return result; - } - - /** - * Blocking call to get the current WifiConfiguration by a privileged caller so private data, - * like the password, is not redacted. - * - * @param channel AsyncChannel to use for the response - * @return List list of configured networks configs - */ - public List<WifiConfiguration> syncGetPrivilegedConfiguredNetwork(AsyncChannel channel) { - Message resultMsg = channel.sendMessageSynchronously( - CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS); - if (messageIsNull(resultMsg)) return null; - List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj; - resultMsg.recycle(); - return result; - } - - /** - * Returns the list of FQDN (Fully Qualified Domain Name) to installed Passpoint configurations. - * - * Return the map of all matching configurations with corresponding scanResults (or an empty map - * if none). - * - * @param scanResults The list of scan results - * @return Map that consists of FQDN (Fully Qualified Domain Name) and corresponding - * scanResults per network type({@link WifiManager#PASSPOINT_HOME_NETWORK} and {@link - * WifiManager#PASSPOINT_ROAMING_NETWORK}). - */ - @NonNull - Map<String, Map<Integer, List<ScanResult>>> syncGetAllMatchingFqdnsForScanResults( - List<ScanResult> scanResults, - AsyncChannel channel) { - Message resultMsg = channel.sendMessageSynchronously( - CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS, - scanResults); - if (messageIsNull(resultMsg)) return new HashMap<>(); - Map<String, Map<Integer, List<ScanResult>>> configs = - (Map<String, Map<Integer, List<ScanResult>>>) resultMsg.obj; - resultMsg.recycle(); - return configs; - } - - /** - * Retrieve a list of {@link OsuProvider} associated with the given list of ScanResult - * synchronously. - * - * @param scanResults a list of ScanResult that has Passpoint APs. - * @param channel Channel for communicating with the state machine - * @return Map that consists of {@link OsuProvider} and a matching list of {@link ScanResult}. - */ - @NonNull - public Map<OsuProvider, List<ScanResult>> syncGetMatchingOsuProviders( - List<ScanResult> scanResults, - AsyncChannel channel) { - Message resultMsg = - channel.sendMessageSynchronously(CMD_GET_MATCHING_OSU_PROVIDERS, scanResults); - if (messageIsNull(resultMsg)) return new HashMap<>(); - Map<OsuProvider, List<ScanResult>> providers = - (Map<OsuProvider, List<ScanResult>>) resultMsg.obj; - resultMsg.recycle(); - return providers; - } - - /** - * Returns the matching Passpoint configurations for given OSU(Online Sign-Up) Providers - * - * @param osuProviders a list of {@link OsuProvider} - * @param channel AsyncChannel to use for the response - * @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}. - */ - @NonNull - public Map<OsuProvider, PasspointConfiguration> syncGetMatchingPasspointConfigsForOsuProviders( - List<OsuProvider> osuProviders, AsyncChannel channel) { - Message resultMsg = - channel.sendMessageSynchronously( - CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS, osuProviders); - if (messageIsNull(resultMsg)) return new HashMap<>(); - Map<OsuProvider, PasspointConfiguration> result = - (Map<OsuProvider, PasspointConfiguration>) resultMsg.obj; - resultMsg.recycle(); - return result; - } - - /** - * Returns the corresponding wifi configurations for given FQDN (Fully Qualified Domain Name) - * list. - * - * An empty list will be returned when no match is found. - * - * @param fqdnList a list of FQDN - * @param channel AsyncChannel to use for the response - * @return List of {@link WifiConfiguration} converted from - * {@link com.android.server.wifi.hotspot2.PasspointProvider} - */ - @NonNull - public List<WifiConfiguration> syncGetWifiConfigsForPasspointProfiles(List<String> fqdnList, - AsyncChannel channel) { - Message resultMsg = - channel.sendMessageSynchronously( - CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES, fqdnList); - if (messageIsNull(resultMsg)) return new ArrayList<>(); - List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj; - resultMsg.recycle(); - return result; - } - - /** - * Add or update a Passpoint configuration synchronously. - * - * @param channel Channel for communicating with the state machine - * @param config The configuration to add or update - * @param packageName Package name of the app adding/updating {@code config}. - * @return true on success - */ - public boolean syncAddOrUpdatePasspointConfig(AsyncChannel channel, - PasspointConfiguration config, int uid, String packageName) { - Bundle bundle = new Bundle(); - bundle.putInt(EXTRA_UID, uid); - bundle.putString(EXTRA_PACKAGE_NAME, packageName); - bundle.putParcelable(EXTRA_PASSPOINT_CONFIGURATION, config); - Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG, - bundle); - if (messageIsNull(resultMsg)) return false; - boolean result = (resultMsg.arg1 == SUCCESS); - resultMsg.recycle(); - return result; - } - - /** - * Remove a Passpoint configuration synchronously. - * - * @param channel Channel for communicating with the state machine - * @param privileged Whether the caller is a privileged entity - * @param fqdn The FQDN of the Passpoint configuration to remove - * @return true on success - */ - public boolean syncRemovePasspointConfig(AsyncChannel channel, boolean privileged, - String fqdn) { - Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_PASSPOINT_CONFIG, - privileged ? 1 : 0, 0, fqdn); - if (messageIsNull(resultMsg)) return false; - 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 - * @param privileged Whether the caller is a privileged entity - * @return List of {@link PasspointConfiguration} - */ - public List<PasspointConfiguration> syncGetPasspointConfigs(AsyncChannel channel, - boolean privileged) { - Message resultMsg = channel.sendMessageSynchronously(CMD_GET_PASSPOINT_CONFIGS, - privileged ? 1 : 0); - if (messageIsNull(resultMsg)) return null; - List<PasspointConfiguration> result = (List<PasspointConfiguration>) resultMsg.obj; - resultMsg.recycle(); - return result; - } - - /** * Start subscription provisioning synchronously * * @param provider {@link OsuProvider} the provider to provision with @@ -1864,49 +1649,6 @@ public class ClientModeImpl extends StateMachine { } /** - * Delete a network - * - * @param networkId id of the network to be removed - */ - public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) { - Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId); - if (messageIsNull(resultMsg)) return false; - boolean result = (resultMsg.arg1 != FAILURE); - resultMsg.recycle(); - return result; - } - - /** - * Enable a network - * - * @param netId network id of the network - * @param disableOthers true, if all other networks have to be disabled - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) { - Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId, - disableOthers ? 1 : 0); - if (messageIsNull(resultMsg)) return false; - boolean result = (resultMsg.arg1 != FAILURE); - resultMsg.recycle(); - return result; - } - - /** - * Disable a network - * - * @param netId network id of the network - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public boolean syncDisableNetwork(AsyncChannel channel, int netId) { - Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId); - boolean result = (resultMsg.what != WifiManager.DISABLE_NETWORK_FAILED); - if (messageIsNull(resultMsg)) return false; - resultMsg.recycle(); - return result; - } - - /** * Method to enable/disable RSSI polling * @param enabled boolean idicating if polling should start */ @@ -1963,24 +1705,6 @@ public class ClientModeImpl extends StateMachine { } /** - * Send a message indicating a package has been uninstalled. - */ - public void removeAppConfigs(String packageName, int uid) { - // Build partial AppInfo manually - package may not exist in database any more - ApplicationInfo ai = new ApplicationInfo(); - ai.packageName = packageName; - ai.uid = uid; - sendMessage(CMD_REMOVE_APP_CONFIGURATIONS, ai); - } - - /** - * Send a message indicating a user has been removed. - */ - public void removeUserConfigs(int userId) { - sendMessage(CMD_REMOVE_USER_CONFIGURATIONS, userId); - } - - /** * Update the BatteryStats WorkSource. */ public void updateBatteryWorkSource(WorkSource newSource) { @@ -2070,27 +1794,6 @@ public class ClientModeImpl extends StateMachine { } /** - * Trigger message to handle user switch event. - */ - public void handleUserSwitch(int userId) { - sendMessage(CMD_USER_SWITCH, userId); - } - - /** - * Trigger message to handle user unlock event. - */ - public void handleUserUnlock(int userId) { - sendMessage(CMD_USER_UNLOCK, userId); - } - - /** - * Trigger message to handle user stop event. - */ - public void handleUserStop(int userId) { - sendMessage(CMD_USER_STOP, userId); - } - - /** * ****************************************************** * Internal private functions * ****************************************************** @@ -2144,12 +1847,14 @@ public class ClientModeImpl extends StateMachine { sb.append(stateChangeResult.toString()); } break; - case WifiManager.SAVE_NETWORK: + case CMD_CONNECT_NETWORK: + case CMD_SAVE_NETWORK: { + NetworkUpdateResult result = (NetworkUpdateResult) msg.obj; sb.append(" "); - sb.append(Integer.toString(msg.arg1)); + sb.append(Integer.toString(result.netId)); sb.append(" "); sb.append(Integer.toString(msg.arg2)); - config = (WifiConfiguration) msg.obj; + config = mWifiConfigManager.getConfiguredNetwork(result.netId); if (config != null) { sb.append(" ").append(config.configKey()); sb.append(" nid=").append(config.networkId); @@ -2170,35 +1875,7 @@ public class ClientModeImpl extends StateMachine { sb.append(" suid=").append(config.lastUpdateUid); } break; - case WifiManager.FORGET_NETWORK: - sb.append(" "); - sb.append(Integer.toString(msg.arg1)); - sb.append(" "); - sb.append(Integer.toString(msg.arg2)); - 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 (config.preSharedKey != null) { - sb.append(" hasPSK"); - } - if (config.ephemeral) { - sb.append(" ephemeral"); - } - if (config.selfAdded) { - sb.append(" selfAdded"); - } - sb.append(" cuid=").append(config.creatorUid); - sb.append(" suid=").append(config.lastUpdateUid); - WifiConfiguration.NetworkSelectionStatus netWorkSelectionStatus = - config.getNetworkSelectionStatus(); - sb.append(" ajst=").append( - netWorkSelectionStatus.getNetworkStatusString()); - } - break; + } case WifiMonitor.ASSOCIATION_REJECTION_EVENT: sb.append(" "); sb.append(" timedOut=" + Integer.toString(msg.arg1)); @@ -2227,8 +1904,8 @@ public class ClientModeImpl extends StateMachine { sb.append(" last=").append(key); } break; - case CMD_TARGET_BSSID: - case CMD_ASSOCIATED_BSSID: + case WifiMonitor.TARGET_BSSID_EVENT: + case WifiMonitor.ASSOCIATED_BSSID_EVENT: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); @@ -2258,7 +1935,7 @@ public class ClientModeImpl extends StateMachine { case CMD_RSSI_POLL: case CMD_ONESHOT_RSSI_POLL: case CMD_UNWANTED_NETWORK: - case WifiManager.RSSI_PKTCNT_FETCH: + case CMD_PKT_CNT_FETCH: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); @@ -2287,7 +1964,6 @@ public class ClientModeImpl extends StateMachine { sb.append(String.format(" score=%d", mWifiInfo.score)); break; case CMD_START_CONNECT: - case WifiManager.CONNECT_NETWORK: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); @@ -2329,53 +2005,6 @@ public class ClientModeImpl extends StateMachine { sb.append(" roam=").append(Boolean.toString(mIsAutoRoaming)); sb.append(" fail count=").append(Integer.toString(mRoamFailCount)); break; - case CMD_ADD_OR_UPDATE_NETWORK: - sb.append(" "); - sb.append(Integer.toString(msg.arg1)); - sb.append(" "); - sb.append(Integer.toString(msg.arg2)); - if (msg.obj != null) { - config = (WifiConfiguration) msg.obj; - sb.append(" ").append(config.configKey()); - sb.append(" prio=").append(config.priority); - sb.append(" status=").append(config.status); - if (config.BSSID != null) { - sb.append(" ").append(config.BSSID); - } - WifiConfiguration curConfig = getCurrentWifiConfiguration(); - if (curConfig != null) { - if (curConfig.configKey().equals(config.configKey())) { - sb.append(" is current"); - } else { - sb.append(" current=").append(curConfig.configKey()); - sb.append(" prio=").append(curConfig.priority); - sb.append(" status=").append(curConfig.status); - } - } - } - break; - case WifiManager.DISABLE_NETWORK: - case CMD_ENABLE_NETWORK: - sb.append(" "); - sb.append(Integer.toString(msg.arg1)); - sb.append(" "); - sb.append(Integer.toString(msg.arg2)); - key = mWifiConfigManager.getLastSelectedNetworkConfigKey(); - if (key != null) { - sb.append(" last=").append(key); - } - config = mWifiConfigManager.getConfiguredNetwork(msg.arg1); - if (config != null && (key == null || !config.configKey().equals(key))) { - sb.append(" target=").append(key); - } - break; - case CMD_GET_CONFIGURED_NETWORKS: - sb.append(" "); - sb.append(Integer.toString(msg.arg1)); - sb.append(" "); - sb.append(Integer.toString(msg.arg2)); - sb.append(" num=").append(mWifiConfigManager.getConfiguredNetworks().size()); - break; case CMD_PRE_DHCP_ACTION: sb.append(" "); sb.append(Integer.toString(msg.arg1)); @@ -2472,10 +2101,6 @@ public class ClientModeImpl extends StateMachine { sb.append(" thresholds="); sb.append(Arrays.toString(mRssiRanges)); break; - case CMD_USER_SWITCH: - sb.append(" userId="); - sb.append(Integer.toString(msg.arg1)); - break; case CMD_IPV4_PROVISIONING_SUCCESS: sb.append(" "); sb.append(/* DhcpResults */ msg.obj); @@ -2504,17 +2129,11 @@ public class ClientModeImpl extends StateMachine { case AsyncChannel.CMD_CHANNEL_DISCONNECTED: s = "CMD_CHANNEL_DISCONNECTED"; break; - case WifiManager.DISABLE_NETWORK: - s = "DISABLE_NETWORK"; + case CMD_CONNECT_NETWORK: + s = "CMD_CONNECT_NETWORK"; break; - case WifiManager.CONNECT_NETWORK: - s = "CONNECT_NETWORK"; - break; - case WifiManager.SAVE_NETWORK: - s = "SAVE_NETWORK"; - break; - case WifiManager.FORGET_NETWORK: - s = "FORGET_NETWORK"; + case CMD_SAVE_NETWORK: + s = "CMD_SAVE_NETWORK"; break; case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: s = "SUPPLICANT_STATE_CHANGE_EVENT"; @@ -2567,8 +2186,8 @@ public class ClientModeImpl extends StateMachine { case WifiP2pServiceImpl.BLOCK_DISCOVERY: s = "BLOCK_DISCOVERY"; break; - case WifiManager.RSSI_PKTCNT_FETCH: - s = "RSSI_PKTCNT_FETCH"; + case CMD_PKT_CNT_FETCH: + s = "CMD_PKT_CNT_FETCH"; break; default: s = "what:" + Integer.toString(what); @@ -2582,8 +2201,7 @@ public class ClientModeImpl extends StateMachine { if (mVerboseLoggingEnabled) { logd(" handleScreenStateChanged Enter: screenOn=" + screenOn + " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt - + " state " + getCurrentState().getName() - + " suppState:" + mSupplicantStateTracker.getSupplicantStateName()); + + " state " + getCurrentState().getName()); } enableRssiPolling(screenOn); if (mUserWantsSuspendOpt.get()) { @@ -2912,11 +2530,15 @@ public class ClientModeImpl extends StateMachine { mWifiInfo.setNetworkId(stateChangeResult.networkId); mWifiInfo.setBSSID(stateChangeResult.BSSID); mWifiInfo.setSSID(stateChangeResult.wifiSsid); + if (state == SupplicantState.ASSOCIATED) { + mWifiInfo.setWifiTechnology(mWifiNative.getWifiTechnology(mInterfaceName)); + } } else { // Reset parameters according to WifiInfo.reset() mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); mWifiInfo.setBSSID(null); mWifiInfo.setSSID(null); + mWifiInfo.setWifiTechnology(WifiInfo.WIFI_TECHNOLOGY_UNKNOWN); } updateL2KeyAndGroupHint(); // SSID might have been updated, so call updateCapabilities @@ -2946,8 +2568,6 @@ public class ClientModeImpl extends StateMachine { } } } - - mSupplicantStateTracker.sendMessage(Message.obtain(message)); mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo); return state; } @@ -3388,8 +3008,7 @@ public class ClientModeImpl extends StateMachine { String currentMacString = mWifiNative.getMacAddress(mInterfaceName); MacAddress currentMac = currentMacString == null ? null : MacAddress.fromString(currentMacString); - MacAddress newMac = config.getOrCreateRandomizedMacAddress(); - mWifiConfigManager.setNetworkRandomizedMacAddress(config.networkId, newMac); + MacAddress newMac = mWifiConfigManager.getRandomizedMacAndUpdateIfNeeded(config); if (!WifiConfiguration.isValidMacAddressForRandomization(newMac)) { Log.wtf(TAG, "Config generated an invalid MAC address"); } else if (newMac.equals(currentMac)) { @@ -3453,6 +3072,9 @@ public class ClientModeImpl extends StateMachine { @Override public boolean processMessage(Message message) { boolean handleStatus = HANDLED; + int callbackIdentifier = -1; + int netId; + boolean ok; switch (message.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { @@ -3484,36 +3106,6 @@ public class ClientModeImpl extends StateMachine { mBluetoothConnectionActive = (message.arg1 != BluetoothAdapter.STATE_DISCONNECTED); break; - case CMD_ENABLE_NETWORK: - boolean disableOthers = message.arg2 == 1; - int netId = message.arg1; - boolean ok = mWifiConfigManager.enableNetwork( - netId, disableOthers, message.sendingUid); - if (!ok) { - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - } - replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); - break; - case CMD_ADD_OR_UPDATE_NETWORK: - WifiConfiguration config = (WifiConfiguration) message.obj; - NetworkUpdateResult result = - mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); - if (!result.isSuccess()) { - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - } - replyToMessage(message, message.what, result.getNetworkId()); - break; - case CMD_REMOVE_NETWORK: - deleteNetworkConfigAndSendReply(message, false); - break; - case CMD_GET_CONFIGURED_NETWORKS: - replyToMessage(message, message.what, - mWifiConfigManager.getSavedNetworks(message.arg2)); - break; - case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS: - replyToMessage(message, message.what, - mWifiConfigManager.getConfiguredNetworksWithPasswords()); - break; case CMD_ENABLE_RSSI_POLL: mEnableRssiPolling = (message.arg1 == 1); break; @@ -3526,17 +3118,11 @@ public class ClientModeImpl extends StateMachine { break; case CMD_INITIALIZE: ok = mWifiNative.initialize(); - mPasspointManager.initializeProvisioner( - mWifiInjector.getWifiServiceHandlerThread().getLooper()); replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); break; case CMD_BOOT_COMPLETED: // get other services that we need to manage getAdditionalWifiServiceInterfaces(); - new MemoryStoreImpl(mContext, mWifiInjector, mWifiScoreCard).start(); - if (!mWifiConfigManager.loadFromStore()) { - Log.e(TAG, "Failed to load from config store"); - } registerNetworkFactory(); break; case CMD_SCREEN_STATE_CHANGED: @@ -3557,16 +3143,13 @@ public class ClientModeImpl extends StateMachine { case CMD_POST_DHCP_ACTION: case WifiMonitor.SUP_REQUEST_IDENTITY: case WifiMonitor.SUP_REQUEST_SIM_AUTH: - case CMD_TARGET_BSSID: + case WifiMonitor.TARGET_BSSID_EVENT: case CMD_START_CONNECT: case CMD_START_ROAM: - case CMD_ASSOCIATED_BSSID: + case WifiMonitor.ASSOCIATED_BSSID_EVENT: case CMD_UNWANTED_NETWORK: case CMD_DISCONNECTING_WATCHDOG_TIMER: case CMD_ROAM_WATCHDOG_TIMER: - case CMD_DISABLE_EPHEMERAL_NETWORK: - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; - break; case CMD_SET_OPERATIONAL_MODE: // using the CMD_SET_OPERATIONAL_MODE (sent at front of queue) to trigger the // state transitions performed in setOperationalMode. @@ -3581,23 +3164,19 @@ public class ClientModeImpl extends StateMachine { setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false); } break; - case WifiManager.CONNECT_NETWORK: - replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, - WifiManager.BUSY); + case CMD_CONNECT_NETWORK: + // wifi off, can't connect. + callbackIdentifier = message.arg2; + sendActionListenerFailure(callbackIdentifier, WifiManager.BUSY); break; - case WifiManager.FORGET_NETWORK: - deleteNetworkConfigAndSendReply(message, true); + case CMD_SAVE_NETWORK: + // wifi off, nothing more to do here. + callbackIdentifier = message.arg2; + sendActionListenerSuccess(callbackIdentifier); break; - case WifiManager.SAVE_NETWORK: - saveNetworkConfigAndSendReply(message); - break; - case WifiManager.DISABLE_NETWORK: - replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED, - WifiManager.BUSY); - break; - case WifiManager.RSSI_PKTCNT_FETCH: - replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED, - WifiManager.BUSY); + case CMD_PKT_CNT_FETCH: + callbackIdentifier = message.arg2; + sendTxPacketCountListenerFailure(callbackIdentifier, WifiManager.BUSY); break; case CMD_GET_SUPPORTED_FEATURES: long featureSet = (mWifiNative.getSupportedFeatureSet(mInterfaceName)); @@ -3619,16 +3198,6 @@ public class ClientModeImpl extends StateMachine { case CMD_UPDATE_LINKPROPERTIES: updateLinkProperties((LinkProperties) message.obj); break; - case CMD_GET_MATCHING_OSU_PROVIDERS: - replyToMessage(message, message.what, new HashMap<>()); - break; - case CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS: - replyToMessage(message, message.what, - new HashMap<OsuProvider, PasspointConfiguration>()); - break; - case CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES: - replyToMessage(message, message.what, new ArrayList<>()); - break; case CMD_START_SUBSCRIPTION_PROVISIONING: replyToMessage(message, message.what, 0); break; @@ -3637,12 +3206,6 @@ public class ClientModeImpl extends StateMachine { case CMD_IP_REACHABILITY_LOST: mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; break; - case CMD_REMOVE_APP_CONFIGURATIONS: - deferMessage(message); - break; - case CMD_REMOVE_USER_CONFIGURATIONS: - deferMessage(message); - break; case CMD_START_IP_PACKET_OFFLOAD: /* fall-through */ case CMD_STOP_IP_PACKET_OFFLOAD: @@ -3659,47 +3222,12 @@ public class ClientModeImpl extends StateMachine { case CMD_STOP_RSSI_MONITORING_OFFLOAD: mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; break; - case CMD_USER_SWITCH: - 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_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 * (message.what is not looked at) */ replyToMessage(message, message.what); break; - case CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG: - Bundle bundle = (Bundle) message.obj; - int addResult = mPasspointManager.addOrUpdateProvider(bundle.getParcelable( - EXTRA_PASSPOINT_CONFIGURATION), - bundle.getInt(EXTRA_UID), - bundle.getString(EXTRA_PACKAGE_NAME)) - ? SUCCESS : FAILURE; - replyToMessage(message, message.what, addResult); - break; - case CMD_REMOVE_PASSPOINT_CONFIG: - int removeResult = mPasspointManager.removeProvider( - message.sendingUid, message.arg1 == 1, (String) message.obj) - ? SUCCESS : FAILURE; - replyToMessage(message, message.what, removeResult); - break; - case CMD_GET_PASSPOINT_CONFIGS: - replyToMessage(message, message.what, mPasspointManager.getProviderConfigs( - message.sendingUid, message.arg1 == 1)); - break; case CMD_RESET_SIM_NETWORKS: /* Defer this message until supplicant is started. */ mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED; @@ -3725,9 +3253,6 @@ public class ClientModeImpl extends StateMachine { mWifiDiagnostics.reportConnectionEvent( BaseWifiDiagnostics.CONNECTION_EVENT_TIMEOUT); break; - case CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS: - replyToMessage(message, message.what, new HashMap<>()); - break; case 0: // We want to notice any empty messages (with what == 0) that might crop up. // For example, we may have recycled a message sent to multiple handlers. @@ -3927,8 +3452,6 @@ public class ClientModeImpl extends StateMachine { // Inform metrics that Wifi is Enabled (but not yet connected) mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); mWifiMetrics.logStaEvent(StaEvent.TYPE_WIFI_ENABLED); - // Inform sar manager that wifi is Enabled - mSarManager.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo); } @@ -3947,8 +3470,6 @@ public class ClientModeImpl extends StateMachine { mWifiMetrics.logStaEvent(StaEvent.TYPE_WIFI_DISABLED); // Inform scorecard that wifi is being disabled mWifiScoreCard.noteWifiDisabled(mWifiInfo); - // Inform sar manager that wifi is being disabled - mSarManager.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); if (!mWifiNative.removeAllNetworks(mInterfaceName)) { loge("Failed to remove networks on exiting connect mode"); @@ -3972,6 +3493,7 @@ public class ClientModeImpl extends StateMachine { int reasonCode; boolean timedOut; boolean handleStatus = HANDLED; + int callbackIdentifier = -1; switch (message.what) { case WifiMonitor.ASSOCIATION_REJECTION_EVENT: @@ -3997,7 +3519,6 @@ public class ClientModeImpl extends StateMachine { .DISABLED_ASSOCIATION_REJECTION); mWifiConfigManager.setRecentFailureAssociationStatus(mTargetNetworkId, reasonCode); - mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT); // If rejection occurred while Metrics is tracking a ConnnectionEvent, end it. reportConnectionAttemptEnd( timedOut @@ -4013,7 +3534,6 @@ public class ClientModeImpl extends StateMachine { case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: mWifiDiagnostics.captureBugReportData( WifiDiagnostics.REPORT_REASON_AUTH_FAILURE); - mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT); int disableReason = WifiConfiguration.NetworkSelectionStatus .DISABLED_AUTHENTICATION_FAILURE; reasonCode = message.arg1; @@ -4070,7 +3590,8 @@ public class ClientModeImpl extends StateMachine { if (reasonCode != WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD) { mWifiInjector.getWifiLastResortWatchdog() .noteConnectionFailureAndTriggerIfNeeded( - getTargetSsid(), mTargetRoamBSSID, + getTargetSsid(), + (mLastBssid == null) ? mTargetRoamBSSID : mLastBssid, WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); } break; @@ -4112,59 +3633,6 @@ public class ClientModeImpl extends StateMachine { mTemporarilyDisconnectWifi = false; } break; - case CMD_REMOVE_NETWORK: - if (!deleteNetworkConfigAndSendReply(message, false)) { - // failed to remove the config and caller was notified - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - break; - } - // 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: - boolean disableOthers = message.arg2 == 1; - netId = message.arg1; - if (disableOthers) { - // 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); - } - if (!ok) { - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - } - replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); - break; - case WifiManager.DISABLE_NETWORK: - 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"); - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED, - WifiManager.ERROR); - } - break; - case CMD_DISABLE_EPHEMERAL_NETWORK: - config = mWifiConfigManager.disableEphemeralNetwork((String) message.obj); - if (config != null) { - if (config.networkId == mTargetNetworkId - || config.networkId == mLastNetworkId) { - // Disconnect and let autojoin reselect a new network - sendMessage(CMD_DISCONNECT); - } - } - break; case WifiMonitor.SUP_REQUEST_IDENTITY: netId = message.arg2; boolean identitySent = false; @@ -4179,8 +3647,8 @@ public class ClientModeImpl extends StateMachine { mWifiInjector.getCarrierNetworkConfig()); Log.i(TAG, "SUP_REQUEST_IDENTITY: identityPair=" + identityPair); if (identityPair != null && identityPair.first != null) { - mWifiNative.simIdentityResponse(mInterfaceName, netId, - identityPair.first, identityPair.second); + mWifiNative.simIdentityResponse(mInterfaceName, identityPair.first, + identityPair.second); identitySent = true; } else { Log.e(TAG, "Unable to retrieve identity from Telephony"); @@ -4217,22 +3685,6 @@ public class ClientModeImpl extends StateMachine { loge("Invalid SIM auth request"); } break; - case CMD_GET_MATCHING_OSU_PROVIDERS: - replyToMessage(message, message.what, - mPasspointManager.getMatchingOsuProviders( - (List<ScanResult>) message.obj)); - break; - case CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS: - replyToMessage(message, message.what, - mPasspointManager.getMatchingPasspointConfigsForOsuProviders( - (List<OsuProvider>) message.obj)); - break; - case CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES: - replyToMessage(message, message.what, - mPasspointManager.getWifiConfigsForPasspointProfiles( - (List<String>) message.obj)); - - break; case CMD_START_SUBSCRIPTION_PROVISIONING: IProvisioningCallback callback = (IProvisioningCallback) message.obj; OsuProvider provider = @@ -4270,8 +3722,7 @@ public class ClientModeImpl extends StateMachine { } } config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId); - logd("CMD_START_CONNECT sup state " - + mSupplicantStateTracker.getSupplicantStateName() + logd("CMD_START_CONNECT " + " my state " + getCurrentState().getName() + " nid=" + Integer.toString(netId) + " roam=" + Boolean.toString(mIsAutoRoaming)); @@ -4324,80 +3775,43 @@ public class ClientModeImpl extends StateMachine { WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, WifiMetricsProto.ConnectionEvent.HLF_NONE, WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); - replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, - WifiManager.ERROR); - break; - } - break; - case CMD_REMOVE_APP_CONFIGURATIONS: - removedNetworkIds = - mWifiConfigManager.removeNetworksForApp((ApplicationInfo) message.obj); - if (removedNetworkIds.contains(mTargetNetworkId) - || removedNetworkIds.contains(mLastNetworkId)) { - // Disconnect and let autojoin reselect a new network. - sendMessage(CMD_DISCONNECT); - } - break; - case CMD_REMOVE_USER_CONFIGURATIONS: - removedNetworkIds = - mWifiConfigManager.removeNetworksForUser((Integer) message.arg1); - if (removedNetworkIds.contains(mTargetNetworkId) - || removedNetworkIds.contains(mLastNetworkId)) { - // Disconnect and let autojoin reselect a new network. - sendMessage(CMD_DISCONNECT); - } - break; - case WifiManager.CONNECT_NETWORK: - /** - * 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 - */ - netId = message.arg1; - config = (WifiConfiguration) message.obj; - boolean hasCredentialChanged = false; - // New network addition. - if (config != null) { - result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); - if (!result.isSuccess()) { - loge("CONNECT_NETWORK adding/updating config=" + config + " failed"); - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, - WifiManager.ERROR); - break; - } - netId = result.getNetworkId(); - hasCredentialChanged = result.hasCredentialChanged(); - } - if (!connectToUserSelectNetwork( - netId, message.sendingUid, hasCredentialChanged)) { - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, - WifiManager.NOT_AUTHORIZED); 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: - result = saveNetworkConfigAndSendReply(message); + case CMD_CONNECT_NETWORK: + callbackIdentifier = message.arg2; + result = (NetworkUpdateResult) message.obj; + netId = result.getNetworkId(); + connectToUserSelectNetwork( + netId, message.sendingUid, result.hasCredentialChanged()); + mWifiMetrics.logStaEvent( + StaEvent.TYPE_CONNECT_NETWORK, + mWifiConfigManager.getConfiguredNetwork(netId)); + sendActionListenerSuccess(callbackIdentifier); + break; + case CMD_SAVE_NETWORK: + callbackIdentifier = message.arg2; + result = (NetworkUpdateResult) message.obj; netId = result.getNetworkId(); - if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) { + if (mWifiInfo.getNetworkId() == netId) { if (result.hasCredentialChanged()) { - config = (WifiConfiguration) message.obj; // 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."); + logi("CMD_SAVE_NETWORK credential changed for nid=" + + netId + ". Reconnecting."); startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY); } else { if (result.hasProxyChanged()) { if (mIpClient != null) { log("Reconfiguring proxy on connection"); - mIpClient.setHttpProxy( - getCurrentWifiConfiguration().getHttpProxy()); + WifiConfiguration currentConfig = getCurrentWifiConfiguration(); + if (currentConfig != null) { + mIpClient.setHttpProxy(currentConfig.getHttpProxy()); + } else { + Log.w(TAG, + "CMD_SAVE_NETWORK proxy change - but no current " + + "Wi-Fi config"); + } } } if (result.hasIpChanged()) { @@ -4405,26 +3819,18 @@ public class ClientModeImpl extends StateMachine { // 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); } } + } else if (mWifiInfo.getNetworkId() == WifiConfiguration.INVALID_NETWORK_ID + && result.hasCredentialChanged()) { + logi("CMD_SAVE_NETWORK credential changed for nid=" + + netId + " while disconnected. Connecting."); + startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY); } + sendActionListenerSuccess(callbackIdentifier); break; - case WifiManager.FORGET_NETWORK: - if (!deleteNetworkConfigAndSendReply(message, true)) { - // Caller was notified of failure, nothing else to do - break; - } - // the network was deleted - netId = message.arg1; - if (netId == mTargetNetworkId || netId == mLastNetworkId) { - // Disconnect and let autojoin reselect a new network - sendMessage(CMD_DISCONNECT); - } - break; - case CMD_ASSOCIATED_BSSID: + case WifiMonitor.ASSOCIATED_BSSID_EVENT: // This is where we can confirm the connection BSSID. Use it to find the // right ScanDetail to populate metrics. String someBssid = (String) message.obj; @@ -4436,6 +3842,8 @@ public class ClientModeImpl extends StateMachine { mWifiMetrics.setConnectionScanDetail(scanDetailCache.getScanDetail( someBssid)); } + // Update last associated BSSID + mLastBssid = someBssid; } handleStatus = NOT_HANDLED; break; @@ -4473,14 +3881,25 @@ public class ClientModeImpl extends StateMachine { config.enterpriseConfig.getEapMethod())) { String anonymousIdentity = mWifiNative.getEapAnonymousIdentity(mInterfaceName); - if (mVerboseLoggingEnabled) { - log("EAP Pseudonym: " + anonymousIdentity); - } - if (!TelephonyUtil.isAnonymousAtRealmIdentity(anonymousIdentity)) { + if (!TextUtils.isEmpty(anonymousIdentity) + && !TelephonyUtil + .isAnonymousAtRealmIdentity(anonymousIdentity)) { + String decoratedPseudonym = TelephonyUtil + .decoratePseudonymWith3GppRealm(getTelephonyManager(), + anonymousIdentity); + if (decoratedPseudonym != null) { + anonymousIdentity = decoratedPseudonym; + } + if (mVerboseLoggingEnabled) { + log("EAP Pseudonym: " + anonymousIdentity); + } // Save the pseudonym only if it is a real one config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity); - mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); + } else { + // Clear any stored pseudonyms + config.enterpriseConfig.setAnonymousIdentity(null); } + mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); } sendNetworkStateChangeBroadcast(mLastBssid); transitionTo(mObtainingIpState); @@ -4509,49 +3928,7 @@ public class ClientModeImpl extends StateMachine { ((Bundle) message.obj).getLong(EXTRA_OSU_ICON_QUERY_BSSID), ((Bundle) message.obj).getString(EXTRA_OSU_ICON_QUERY_FILENAME)); break; - case CMD_MATCH_PROVIDER_NETWORK: - // TODO(b/31065385): Passpoint config management. - replyToMessage(message, message.what, 0); - break; - case CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG: - Bundle bundle = (Bundle) message.obj; - PasspointConfiguration passpointConfig = bundle.getParcelable( - EXTRA_PASSPOINT_CONFIGURATION); - if (mPasspointManager.addOrUpdateProvider(passpointConfig, - bundle.getInt(EXTRA_UID), - bundle.getString(EXTRA_PACKAGE_NAME))) { - 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( - message.sendingUid, message.arg1 == 1, fqdn)) { - if (isProviderOwnedNetwork(mTargetNetworkId, fqdn) - || isProviderOwnedNetwork(mLastNetworkId, fqdn)) { - logd("Disconnect from current network since its provider is removed"); - sendMessage(CMD_DISCONNECT); - } - mWifiConfigManager.removePasspointConfiguredNetwork(fqdn); - replyToMessage(message, message.what, SUCCESS); - } else { - replyToMessage(message, message.what, FAILURE); - } - break; - case CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS: - replyToMessage(message, message.what, - mPasspointManager.getAllMatchingFqdnsForScanResults( - (List<ScanResult>) message.obj)); - break; - case CMD_TARGET_BSSID: + case WifiMonitor.TARGET_BSSID_EVENT: // Trying to associate to this BSSID if (message.obj != null) { mTargetRoamBSSID = (String) message.obj; @@ -4717,25 +4094,6 @@ public class ClientModeImpl extends StateMachine { mNetworkAgent.sendNetworkCapabilities(getCapabilities(currentWifiConfiguration)); } - /** - * 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 void handleEapAuthFailure(int networkId, int errorCode) { WifiConfiguration targetedNetwork = mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId); @@ -4883,47 +4241,6 @@ public class ClientModeImpl extends StateMachine { sendMessage(CMD_NETWORK_STATUS, status); } - // rfc4186 & rfc4187: - // create Permanent Identity base on IMSI, - // identity = usernam@realm - // with username = prefix | IMSI - // and realm is derived MMC/MNC tuple according 3GGP spec(TS23.003) - private String buildIdentity(int eapMethod, String imsi, String mccMnc) { - String mcc; - String mnc; - String prefix; - - if (imsi == null || imsi.isEmpty()) { - return ""; - } - - if (eapMethod == WifiEnterpriseConfig.Eap.SIM) { - prefix = "1"; - } else if (eapMethod == WifiEnterpriseConfig.Eap.AKA) { - prefix = "0"; - } else if (eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME) { - prefix = "6"; - } else { - // not a valid EapMethod - return ""; - } - - /* extract mcc & mnc from mccMnc */ - if (mccMnc != null && !mccMnc.isEmpty()) { - mcc = mccMnc.substring(0, 3); - mnc = mccMnc.substring(3); - if (mnc.length() == 2) { - mnc = "0" + mnc; - } - } else { - // extract mcc & mnc from IMSI, assume mnc size is 3 - mcc = imsi.substring(0, 3); - mnc = imsi.substring(3, 6); - } - - return prefix + imsi + "@wlan.mnc" + mnc + ".mcc" + mcc + ".3gppnetwork.org"; - } - class L2ConnectedState extends State { class RssiEventHandler implements WifiNative.WifiRssiEventHandler { @Override @@ -4997,6 +4314,7 @@ public class ClientModeImpl extends StateMachine { @Override public boolean processMessage(Message message) { boolean handleStatus = HANDLED; + int callbackIdentifier = -1; switch (message.what) { case CMD_PRE_DHCP_ACTION: @@ -5026,7 +4344,8 @@ public class ClientModeImpl extends StateMachine { handleIPv4Failure(); mWifiInjector.getWifiLastResortWatchdog() .noteConnectionFailureAndTriggerIfNeeded( - getTargetSsid(), mTargetRoamBSSID, + getTargetSsid(), + (mLastBssid == null) ? mTargetRoamBSSID : mLastBssid, WifiLastResortWatchdog.FAILURE_CODE_DHCP); break; } @@ -5056,7 +4375,8 @@ public class ClientModeImpl extends StateMachine { WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); mWifiInjector.getWifiLastResortWatchdog() .noteConnectionFailureAndTriggerIfNeeded( - getTargetSsid(), mTargetRoamBSSID, + getTargetSsid(), + (mLastBssid == null) ? mTargetRoamBSSID : mLastBssid, WifiLastResortWatchdog.FAILURE_CODE_DHCP); transitionTo(mDisconnectingState); break; @@ -5090,15 +4410,6 @@ public class ClientModeImpl extends StateMachine { transitionTo(mDisconnectingState); } 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; - } - handleStatus = NOT_HANDLED; - break; case WifiMonitor.NETWORK_CONNECTION_EVENT: mWifiInfo.setBSSID((String) message.obj); mLastNetworkId = message.arg1; @@ -5170,22 +4481,18 @@ public class ClientModeImpl extends StateMachine { mPollRssiIntervalMsecs); } break; - case WifiManager.RSSI_PKTCNT_FETCH: - RssiPacketCountInfo info = new RssiPacketCountInfo(); - fetchRssiLinkSpeedAndFrequencyNative(); - info.rssi = mWifiInfo.getRssi(); + case CMD_PKT_CNT_FETCH: + callbackIdentifier = message.arg2; WifiNative.TxPacketCounters counters = mWifiNative.getTxPacketCounters(mInterfaceName); if (counters != null) { - info.txgood = counters.txSucceeded; - info.txbad = counters.txFailed; - replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info); + sendTxPacketCountListenerSuccess( + callbackIdentifier, counters.txSucceeded + counters.txFailed); } else { - replyToMessage(message, - WifiManager.RSSI_PKTCNT_FETCH_FAILED, WifiManager.ERROR); + sendTxPacketCountListenerSuccess(callbackIdentifier, WifiManager.ERROR); } break; - case CMD_ASSOCIATED_BSSID: + case WifiMonitor.ASSOCIATED_BSSID_EVENT: if ((String) message.obj == null) { logw("Associated command w/o BSSID"); break; @@ -5349,9 +4656,7 @@ public class ClientModeImpl extends StateMachine { // Stop IpClient in case we're switching from DHCP to static // configuration or vice versa. // - // TODO: Only ever enter this state the first time we connect to a - // network, never on switching between static configuration and - // DHCP. When we transition from static configuration to DHCP in + // When we transition from static configuration to DHCP in // particular, we must tell ConnectivityService that we're // disconnected, because DHCP might take a long time during which // connectivity APIs such as getActiveNetworkInfo should not return @@ -5393,19 +4698,17 @@ public class ClientModeImpl extends StateMachine { boolean handleStatus = HANDLED; switch(message.what) { - case CMD_START_CONNECT: - case CMD_START_ROAM: - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; - break; - case WifiManager.SAVE_NETWORK: - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED; - deferMessage(message); - break; case WifiMonitor.NETWORK_DISCONNECTION_EVENT: reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, WifiMetricsProto.ConnectionEvent.HLF_NONE, WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); + mWifiInjector.getWifiLastResortWatchdog() + .noteConnectionFailureAndTriggerIfNeeded( + getTargetSsid(), + (message.obj == null) + ? mTargetRoamBSSID : (String) message.obj, + WifiLastResortWatchdog.FAILURE_CODE_DHCP); handleStatus = NOT_HANDLED; break; case CMD_SET_HIGH_PERF_MODE: @@ -5679,8 +4982,7 @@ public class ClientModeImpl extends StateMachine { mWifiConfigManager.setNetworkValidatedInternetAccess( config.networkId, false); mWifiConfigManager.updateNetworkSelectionStatus(config.networkId, - WifiConfiguration.NetworkSelectionStatus - .DISABLED_NO_INTERNET_PERMANENT); + DISABLED_NO_INTERNET_PERMANENT); } else { // stop collect last-mile stats since validation fail removeMessages(CMD_DIAGS_CONNECT_TIMEOUT); @@ -5696,8 +4998,7 @@ public class ClientModeImpl extends StateMachine { + "no-internet access"); mWifiConfigManager.updateNetworkSelectionStatus( config.networkId, - WifiConfiguration.NetworkSelectionStatus - .DISABLED_NO_INTERNET_TEMPORARY); + DISABLED_NO_INTERNET_TEMPORARY); } } } @@ -5726,7 +5027,7 @@ public class ClientModeImpl extends StateMachine { boolean accept = (message.arg1 != 0); mWifiConfigManager.setNetworkNoInternetAccessExpected(mLastNetworkId, accept); break; - case CMD_ASSOCIATED_BSSID: + case WifiMonitor.ASSOCIATED_BSSID_EVENT: // ASSOCIATING to a new BSSID while already connected, indicates // that driver is roaming mLastDriverRoamAttempt = mClock.getWallClockMillis(); @@ -5780,7 +5081,6 @@ public class ClientModeImpl extends StateMachine { mTargetNetworkId = netId; logd("CMD_START_ROAM sup state " - + mSupplicantStateTracker.getSupplicantStateName() + " my state " + getCurrentState().getName() + " nid=" + Integer.toString(netId) + " config " + config.configKey() @@ -5800,8 +5100,6 @@ public class ClientModeImpl extends StateMachine { WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, WifiMetricsProto.ConnectionEvent.HLF_NONE, WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); - replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, - WifiManager.ERROR); mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; break; } @@ -5855,6 +5153,11 @@ public class ClientModeImpl extends StateMachine { boolean handleStatus = HANDLED; switch (message.what) { + case CMD_CONNECT_NETWORK: + case CMD_SAVE_NETWORK: + mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED; + deferMessage(message); + break; case CMD_DISCONNECT: if (mVerboseLoggingEnabled) { log("Ignore CMD_DISCONNECT when already disconnecting."); @@ -5873,6 +5176,7 @@ public class ClientModeImpl extends StateMachine { * we have missed the network disconnection, transition to mDisconnectedState * and handle the rest of the events there */ + mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED; deferMessage(message); handleNetworkDisconnect(); transitionTo(mDisconnectedState); @@ -6047,7 +5351,8 @@ public class ClientModeImpl extends StateMachine { intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_SSID, config.SSID); intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_EVENT_TYPE, wifiCredentialEventType); - mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT, + // TODO (b/142234604): This will not work on multi-user device scenarios. + mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF, android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE); } } @@ -6087,12 +5392,11 @@ public class ClientModeImpl extends StateMachine { } } if (response == null || response.length() == 0) { - mWifiNative.simAuthFailedResponse(mInterfaceName, requestData.networkId); + mWifiNative.simAuthFailedResponse(mInterfaceName); } else { logv("Supplicant Response -" + response); mWifiNative.simAuthResponse( - mInterfaceName, requestData.networkId, - WifiNative.SIM_AUTH_RESP_TYPE_GSM_AUTH, response); + mInterfaceName, WifiNative.SIM_AUTH_RESP_TYPE_GSM_AUTH, response); } } @@ -6110,9 +5414,9 @@ public class ClientModeImpl extends StateMachine { TelephonyUtil.get3GAuthResponse(requestData, getTelephonyManager()); if (response != null) { mWifiNative.simAuthResponse( - mInterfaceName, requestData.networkId, response.type, response.response); + mInterfaceName, response.type, response.response); } else { - mWifiNative.umtsAuthFailedResponse(mInterfaceName, requestData.networkId); + mWifiNative.umtsAuthFailedResponse(mInterfaceName); } } @@ -6165,90 +5469,9 @@ public class ClientModeImpl extends StateMachine { || reason == 34; // DISASSOC_LOW_ACK } - /** - * Update WifiMetrics before dumping - */ - public void updateWifiMetrics() { - mWifiMetrics.updateSavedNetworks(mWifiConfigManager.getSavedNetworks(Process.WIFI_UID)); - mPasspointManager.updateMetrics(); - } - - /** - * 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"); - } - - 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; - } - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, message.what, FAILURE); - return false; - } - } - - /** - * Private method to handle calling WifiConfigManager to add & enable network configs and reply - * to the message from the sender of the outcome. - * - * @return NetworkUpdateResult with networkId of the added/updated configuration. Will return - * {@link WifiConfiguration#INVALID_NETWORK_ID} in case of error. - */ - private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) { - WifiConfiguration config = (WifiConfiguration) message.obj; - if (config == null) { - loge("SAVE_NETWORK with null configuration " - + mSupplicantStateTracker.getSupplicantStateName() - + " my state " + getCurrentState().getName()); - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); - return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); - } - NetworkUpdateResult result = - mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); - if (!result.isSuccess()) { - loge("SAVE_NETWORK adding/updating config=" + config + " failed"); - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); - return result; - } - if (!mWifiConfigManager.enableNetwork( - result.getNetworkId(), false, message.sendingUid)) { - loge("SAVE_NETWORK enabling config=" + config + " failed"); - mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); - return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); - } - broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); - replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED); - return result; - } - private static String getLinkPropertiesSummary(LinkProperties lp) { List<String> attributes = new ArrayList<>(6); - if (lp.hasIPv4Address()) { + if (lp.hasIpv4Address()) { attributes.add("v4"); } if (lp.hasIPv4DefaultRoute()) { @@ -6257,10 +5480,10 @@ public class ClientModeImpl extends StateMachine { if (lp.hasIPv4DnsServer()) { attributes.add("v4dns"); } - if (lp.hasGlobalIPv6Address()) { + if (lp.hasGlobalIpv6Address()) { attributes.add("v6"); } - if (lp.hasIPv6DefaultRoute()) { + if (lp.hasIpv6DefaultRoute()) { attributes.add("v6r"); } if (lp.hasIPv6DnsServer()) { @@ -6413,4 +5636,178 @@ public class ClientModeImpl extends StateMachine { mWifiNative.probeLink(mInterfaceName, MacAddress.fromString(mWifiInfo.getBSSID()), callback, mcs); } + + private void sendActionListenerFailure(int callbackIdentifier, int reason) { + IActionListener actionListener; + synchronized (mProcessingActionListeners) { + actionListener = mProcessingActionListeners.remove(callbackIdentifier); + } + if (actionListener != null) { + try { + actionListener.onFailure(reason); + } catch (RemoteException e) { + // no-op (client may be dead, nothing to be done) + } + } + } + + private void sendActionListenerSuccess(int callbackIdentifier) { + IActionListener actionListener; + synchronized (mProcessingTxPacketCountListeners) { + actionListener = mProcessingActionListeners.remove(callbackIdentifier); + } + if (actionListener != null) { + try { + actionListener.onSuccess(); + } catch (RemoteException e) { + // no-op (client may be dead, nothing to be done) + } + } + } + + private void sendTxPacketCountListenerFailure(int callbackIdentifier, int reason) { + ITxPacketCountListener txPacketCountListener; + synchronized (mProcessingTxPacketCountListeners) { + txPacketCountListener = mProcessingTxPacketCountListeners.remove(callbackIdentifier); + } + if (txPacketCountListener != null) { + try { + txPacketCountListener.onFailure(reason); + } catch (RemoteException e) { + // no-op (client may be dead, nothing to be done) + } + } + } + + private void sendTxPacketCountListenerSuccess(int callbackIdentifier, int count) { + ITxPacketCountListener txPacketCountListener; + synchronized (mProcessingActionListeners) { + txPacketCountListener = mProcessingTxPacketCountListeners.remove(callbackIdentifier); + } + if (txPacketCountListener != null) { + try { + txPacketCountListener.onSuccess(count); + } catch (RemoteException e) { + // no-op (client may be dead, nothing to be done) + } + } + } + + /** + * Trigger network connection and provide status via the provided callback. + */ + public void connect(WifiConfiguration config, int netId, @Nullable IBinder binder, + @Nullable IActionListener callback, int callbackIdentifier, int callingUid) { + mWifiInjector.getWifiThreadRunner().post(() -> { + if (callback != null && binder != null) { + mProcessingActionListeners.add(binder, callback, callbackIdentifier); + } + /** + * 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 + */ + NetworkUpdateResult result = null; + if (config != null) { + result = mWifiConfigManager.addOrUpdateNetwork(config, callingUid); + if (!result.isSuccess()) { + loge("connectNetwork adding/updating config=" + config + " failed"); + sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR); + return; + } + broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); + } else { + if (mWifiConfigManager.getConfiguredNetwork(netId) == null) { + loge("connectNetwork Invalid network Id=" + netId); + sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR); + return; + } + result = new NetworkUpdateResult(netId); + } + final int networkId = result.getNetworkId(); + if (!mWifiConfigManager.enableNetwork(networkId, true, callingUid, null) + || !mWifiConfigManager.updateLastConnectUid(networkId, callingUid)) { + logi("connect Allowing uid " + callingUid + + " with insufficient permissions to connect=" + networkId); + } else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)) { + // Note user connect choice here, so that it will be considered in the + // next network selection. + mWifiConnectivityManager.setUserConnectChoice(networkId); + } + Message message = + obtainMessage(CMD_CONNECT_NETWORK, -1, callbackIdentifier, result); + message.sendingUid = callingUid; + sendMessage(message); + }); + } + + /** + * Trigger network save and provide status via the provided callback. + */ + public void save(WifiConfiguration config, @Nullable IBinder binder, + @Nullable IActionListener callback, int callbackIdentifier, int callingUid) { + mWifiInjector.getWifiThreadRunner().post(() -> { + if (callback != null && binder != null) { + mProcessingActionListeners.add(binder, callback, callbackIdentifier); + } + if (config == null) { + loge("saveNetwork with null configuration my state " + + getCurrentState().getName()); + sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR); + return; + } + NetworkUpdateResult result = + mWifiConfigManager.addOrUpdateNetwork(config, callingUid); + if (!result.isSuccess()) { + loge("saveNetwork adding/updating config=" + config + " failed"); + sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR); + return; + } + if (!mWifiConfigManager.enableNetwork( + result.getNetworkId(), false, callingUid, null)) { + loge("saveNetwork enabling config=" + config + " failed"); + sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR); + return; + } + broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); + Message message = + obtainMessage(CMD_SAVE_NETWORK, -1 , callbackIdentifier, result); + message.sendingUid = callingUid; + sendMessage(message); + }); + } + + /** + * Trigger network forget and provide status via the provided callback. + */ + public void forget(int netId, @Nullable IBinder binder, @Nullable IActionListener callback, + int callbackIdentifier, int callingUid) { + mWifiInjector.getWifiThreadRunner().post(() -> { + if (callback != null && binder != null) { + mProcessingActionListeners.add(binder, callback, callbackIdentifier); + } + boolean success = mWifiConfigManager.removeNetwork(netId, callingUid, null); + if (!success) { + loge("Failed to remove network"); + sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR); + } + sendActionListenerSuccess(callbackIdentifier); + broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT, null); + }); + } + + /** + * Retrieve tx packet count and provide status via the provided callback. + */ + public void getTxPacketCount(IBinder binder, @NonNull ITxPacketCountListener callback, + int callbackIdentifier, int callingUid) { + mWifiInjector.getWifiThreadRunner().post(() -> { + mProcessingTxPacketCountListeners.add(binder, callback, callbackIdentifier); + + Message message = obtainMessage(CMD_PKT_CNT_FETCH, -1, callbackIdentifier, null); + message.sendingUid = callingUid; + sendMessage(message); + }); + } } diff --git a/service/java/com/android/server/wifi/ClientModeManager.java b/service/java/com/android/server/wifi/ClientModeManager.java index 883b16a610..0206646845 100644 --- a/service/java/com/android/server/wifi/ClientModeManager.java +++ b/service/java/com/android/server/wifi/ClientModeManager.java @@ -44,22 +44,24 @@ public class ClientModeManager implements ActiveModeManager { private final Context mContext; private final WifiNative mWifiNative; - private final WifiMetrics mWifiMetrics; - private final Listener mListener; + private final SarManager mSarManager; + private final WakeupController mWakeupController; + private final Listener mModeListener; private final ClientModeImpl mClientModeImpl; private String mClientInterfaceName; private boolean mIfaceIsUp = false; - private boolean mExpectedStop = false; - ClientModeManager(Context context, @NonNull Looper looper, WifiNative wifiNative, - Listener listener, WifiMetrics wifiMetrics, ClientModeImpl clientModeImpl) { + Listener listener, WifiMetrics wifiMetrics, SarManager sarManager, + WakeupController wakeupController, ClientModeImpl clientModeImpl) { mContext = context; mWifiNative = wifiNative; - mListener = listener; + mModeListener = listener; mWifiMetrics = wifiMetrics; + mSarManager = sarManager; + mWakeupController = wakeupController; mClientModeImpl = clientModeImpl; mStateMachine = new ClientModeStateMachine(looper); } @@ -76,13 +78,12 @@ public class ClientModeManager implements ActiveModeManager { */ public void stop() { Log.d(TAG, " currentstate: " + getCurrentStateName()); - mExpectedStop = true; - if (mClientInterfaceName != null) { + if (isInConnectMode()) { if (mIfaceIsUp) { - updateWifiState(WifiManager.WIFI_STATE_DISABLING, + updateConnectModeState(WifiManager.WIFI_STATE_DISABLING, WifiManager.WIFI_STATE_ENABLED); } else { - updateWifiState(WifiManager.WIFI_STATE_DISABLING, + updateConnectModeState(WifiManager.WIFI_STATE_DISABLING, WifiManager.WIFI_STATE_ENABLING); } } @@ -90,7 +91,27 @@ public class ClientModeManager implements ActiveModeManager { } public @ScanMode int getScanMode() { - return SCAN_WITH_HIDDEN_NETWORKS; + if (isInConnectMode()) { + return SCAN_WITH_HIDDEN_NETWORKS; + } else if (isInScanOnlyMode()) { + return SCAN_WITHOUT_HIDDEN_NETWORKS; + } else { + return SCAN_NONE; + } + } + + /** + * Switch client mode manager to scan only mode. + */ + public void switchToScanOnlyMode() { + mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE); + } + + /** + * Switch client mode manager to connect mode. + */ + public void switchToConnectMode() { + mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE); } /** @@ -102,17 +123,7 @@ public class ClientModeManager implements ActiveModeManager { pw.println("current StateMachine mode: " + getCurrentStateName()); pw.println("mClientInterfaceName: " + mClientInterfaceName); pw.println("mIfaceIsUp: " + mIfaceIsUp); - } - - /** - * Listener for ClientMode state changes. - */ - public interface Listener { - /** - * Invoke when wifi state changes. - * @param state new wifi state - */ - void onStateChanged(int state); + mStateMachine.dump(fd, pw, args); } private String getCurrentStateName() { @@ -130,21 +141,7 @@ public class ClientModeManager implements ActiveModeManager { * @param newState new Wifi state * @param currentState current wifi state */ - private void updateWifiState(int newState, int currentState) { - if (!mExpectedStop) { - mListener.onStateChanged(newState); - } else { - Log.d(TAG, "expected stop, not triggering callbacks: newState = " + newState); - } - - // Once we report the mode has stopped/failed any other stop signals are redundant - // note: this can happen in failure modes where we get multiple callbacks as underlying - // components/interface stops or the underlying interface is destroyed in cleanup - if (newState == WifiManager.WIFI_STATE_UNKNOWN - || newState == WifiManager.WIFI_STATE_DISABLED) { - mExpectedStop = true; - } - + private void updateConnectModeState(int newState, int currentState) { if (newState == WifiManager.WIFI_STATE_UNKNOWN) { // do not need to broadcast failure to system return; @@ -159,14 +156,26 @@ public class ClientModeManager implements ActiveModeManager { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } + public boolean isInConnectMode() { + return mStateMachine.getCurrentState() == mStateMachine.mConnectModeState; + } + + public boolean isInScanOnlyMode() { + return mStateMachine.getCurrentState() == mStateMachine.mScanOnlyModeState; + } + private class ClientModeStateMachine extends StateMachine { // Commands for the state machine. public static final int CMD_START = 0; + public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE = 1; + public static final int CMD_SWITCH_TO_CONNECT_MODE = 2; public static final int CMD_INTERFACE_STATUS_CHANGED = 3; public static final int CMD_INTERFACE_DESTROYED = 4; public static final int CMD_INTERFACE_DOWN = 5; private final State mIdleState = new IdleState(); private final State mStartedState = new StartedState(); + private final State mScanOnlyModeState = new ScanOnlyModeState(); + private final State mConnectModeState = new ConnectModeState(); private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { @Override @@ -176,7 +185,7 @@ public class ClientModeManager implements ActiveModeManager { // we must immediately clean up state in ClientModeImpl to unregister // all client mode related objects - // Note: onDestroyed is only called from the ClientModeImpl thread + // Note: onDestroyed is only called from the main Wifi thread mClientModeImpl.handleIfaceDestroyed(); sendMessage(CMD_INTERFACE_DESTROYED); @@ -201,15 +210,18 @@ public class ClientModeManager implements ActiveModeManager { ClientModeStateMachine(Looper looper) { super(TAG, looper); + // CHECKSTYLE:OFF IndentationCheck addState(mIdleState); addState(mStartedState); + addState(mScanOnlyModeState, mStartedState); + addState(mConnectModeState, mStartedState); + // CHECKSTYLE:ON IndentationCheck setInitialState(mIdleState); start(); } private class IdleState extends State { - @Override public void enter() { Log.d(TAG, "entering IdleState"); @@ -221,21 +233,16 @@ public class ClientModeManager implements ActiveModeManager { public boolean processMessage(Message message) { switch (message.what) { case CMD_START: - updateWifiState(WifiManager.WIFI_STATE_ENABLING, - WifiManager.WIFI_STATE_DISABLED); - + // Always start in scan mode first. mClientInterfaceName = - mWifiNative.setupInterfaceForClientInConnectivityMode( + mWifiNative.setupInterfaceForClientInScanMode( mWifiNativeInterfaceCallback); if (TextUtils.isEmpty(mClientInterfaceName)) { Log.e(TAG, "Failed to create ClientInterface. Sit in Idle"); - updateWifiState(WifiManager.WIFI_STATE_UNKNOWN, - WifiManager.WIFI_STATE_ENABLING); - updateWifiState(WifiManager.WIFI_STATE_DISABLED, - WifiManager.WIFI_STATE_UNKNOWN); + mModeListener.onStartFailure(); break; } - transitionTo(mStartedState); + transitionTo(mScanOnlyModeState); break; default: Log.d(TAG, "received an invalid message: " + message); @@ -252,22 +259,9 @@ public class ClientModeManager implements ActiveModeManager { return; // no change } mIfaceIsUp = isUp; - if (isUp) { - Log.d(TAG, "Wifi is ready to use for client mode"); - mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE, - mClientInterfaceName); - updateWifiState(WifiManager.WIFI_STATE_ENABLED, - WifiManager.WIFI_STATE_ENABLING); - } else { - if (mClientModeImpl.isConnectedMacRandomizationEnabled()) { - // Handle the error case where our underlying interface went down if we - // do not have mac randomization enabled (b/72459123). - return; - } + if (!isUp) { // if the interface goes down we should exit and go back to idle state. Log.d(TAG, "interface down!"); - updateWifiState(WifiManager.WIFI_STATE_UNKNOWN, - WifiManager.WIFI_STATE_ENABLED); mStateMachine.sendMessage(CMD_INTERFACE_DOWN); } } @@ -285,12 +279,33 @@ public class ClientModeManager implements ActiveModeManager { case CMD_START: // Already started, ignore this command. break; + case CMD_SWITCH_TO_CONNECT_MODE: + updateConnectModeState(WifiManager.WIFI_STATE_ENABLING, + WifiManager.WIFI_STATE_DISABLED); + if (!mWifiNative.switchClientInterfaceToConnectivityMode( + mClientInterfaceName)) { + updateConnectModeState(WifiManager.WIFI_STATE_UNKNOWN, + WifiManager.WIFI_STATE_ENABLING); + updateConnectModeState(WifiManager.WIFI_STATE_DISABLED, + WifiManager.WIFI_STATE_UNKNOWN); + mModeListener.onStartFailure(); + break; + } + transitionTo(mConnectModeState); + break; + case CMD_SWITCH_TO_SCAN_ONLY_MODE: + if (!mWifiNative.switchClientInterfaceToScanMode(mClientInterfaceName)) { + mModeListener.onStartFailure(); + break; + } + updateConnectModeState(WifiManager.WIFI_STATE_DISABLING, + WifiManager.WIFI_STATE_ENABLED); + transitionTo(mScanOnlyModeState); + break; case CMD_INTERFACE_DOWN: - Log.e(TAG, "Detected an interface down, reporting failure to SelfRecovery"); + Log.e(TAG, "Detected an interface down, reporting failure to " + + "SelfRecovery"); mClientModeImpl.failureDetected(SelfRecovery.REASON_STA_IFACE_DOWN); - - updateWifiState(WifiManager.WIFI_STATE_DISABLING, - WifiManager.WIFI_STATE_UNKNOWN); transitionTo(mIdleState); break; case CMD_INTERFACE_STATUS_CHANGED: @@ -299,9 +314,6 @@ public class ClientModeManager implements ActiveModeManager { break; case CMD_INTERFACE_DESTROYED: Log.d(TAG, "interface destroyed - client mode stopping"); - - updateWifiState(WifiManager.WIFI_STATE_DISABLING, - WifiManager.WIFI_STATE_ENABLED); mClientInterfaceName = null; transitionTo(mIdleState); break; @@ -324,11 +336,108 @@ public class ClientModeManager implements ActiveModeManager { mIfaceIsUp = false; } - updateWifiState(WifiManager.WIFI_STATE_DISABLED, - WifiManager.WIFI_STATE_DISABLING); - // once we leave started, nothing else to do... stop the state machine mStateMachine.quitNow(); + mModeListener.onStopped(); + } + } + + private class ScanOnlyModeState extends State { + @Override + public void enter() { + Log.d(TAG, "entering ScanOnlyModeState"); + mClientModeImpl.setOperationalMode(ClientModeImpl.SCAN_ONLY_MODE, + mClientInterfaceName); + mModeListener.onStarted(); + + // Inform sar manager that scan only is being enabled + mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); + mWakeupController.start(); + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_SWITCH_TO_SCAN_ONLY_MODE: + // Already in scan only mode, ignore this command. + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + + @Override + public void exit() { + // Inform sar manager that scan only is being disabled + mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_DISABLED); + mWakeupController.stop(); + } + } + + private class ConnectModeState extends State { + @Override + public void enter() { + Log.d(TAG, "entering ConnectModeState"); + mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE, + mClientInterfaceName); + mModeListener.onStarted(); + updateConnectModeState(WifiManager.WIFI_STATE_ENABLED, + WifiManager.WIFI_STATE_ENABLING); + + // Inform sar manager that wifi is Enabled + mSarManager.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_SWITCH_TO_CONNECT_MODE: + // Already in connect mode, ignore this command. + break; + case CMD_SWITCH_TO_SCAN_ONLY_MODE: + updateConnectModeState(WifiManager.WIFI_STATE_DISABLING, + WifiManager.WIFI_STATE_ENABLED); + return NOT_HANDLED; // Handled in StartedState. + case CMD_INTERFACE_DOWN: + updateConnectModeState(WifiManager.WIFI_STATE_DISABLING, + WifiManager.WIFI_STATE_UNKNOWN); + return NOT_HANDLED; // Handled in StartedState. + case CMD_INTERFACE_STATUS_CHANGED: + boolean isUp = message.arg1 == 1; + if (isUp == mIfaceIsUp) { + break; // no change + } + if (!isUp) { + if (!mClientModeImpl.isConnectedMacRandomizationEnabled()) { + // Handle the error case where our underlying interface went down if + // we do not have mac randomization enabled (b/72459123). + // if the interface goes down we should exit and go back to idle + // state. + updateConnectModeState(WifiManager.WIFI_STATE_UNKNOWN, + WifiManager.WIFI_STATE_ENABLED); + } else { + return HANDLED; // For MAC randomization, ignore... + } + } + return NOT_HANDLED; // Handled in StartedState. + case CMD_INTERFACE_DESTROYED: + updateConnectModeState(WifiManager.WIFI_STATE_DISABLING, + WifiManager.WIFI_STATE_ENABLED); + return NOT_HANDLED; // Handled in StartedState. + default: + return NOT_HANDLED; + } + return HANDLED; + } + + @Override + public void exit() { + updateConnectModeState(WifiManager.WIFI_STATE_DISABLED, + WifiManager.WIFI_STATE_DISABLING); + + // Inform sar manager that wifi is being disabled + mSarManager.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); } } } diff --git a/service/java/com/android/server/wifi/CompatibilityScorer.java b/service/java/com/android/server/wifi/CompatibilityScorer.java index 550eca72aa..1bb55b9a24 100644 --- a/service/java/com/android/server/wifi/CompatibilityScorer.java +++ b/service/java/com/android/server/wifi/CompatibilityScorer.java @@ -57,6 +57,8 @@ final class CompatibilityScorer implements WifiCandidates.CandidateScorer { // config_wifi_framework_SAME_BSSID_AWARD public static final int SAME_BSSID_AWARD_IS_24 = 24; + private static final boolean USE_USER_CONNECT_CHOICE = true; + CompatibilityScorer(ScoringParams scoringParams) { mScoringParams = scoringParams; } @@ -95,13 +97,14 @@ final class CompatibilityScorer implements WifiCandidates.CandidateScorer { // The old method breaks ties on the basis of RSSI, which we can // emulate easily since our score does not need to be an integer. double tieBreaker = candidate.getScanRssi() / 1000.0; - return new ScoredCandidate(score + tieBreaker, 10, candidate); + return new ScoredCandidate(score + tieBreaker, 10, + USE_USER_CONNECT_CHOICE, candidate); } @Override - public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> group) { + public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> candidates) { ScoredCandidate choice = ScoredCandidate.NONE; - for (Candidate candidate : group) { + for (Candidate candidate : candidates) { ScoredCandidate scoredCandidate = scoreCandidate(candidate); if (scoredCandidate.value > choice.value) { choice = scoredCandidate; @@ -112,9 +115,4 @@ final class CompatibilityScorer implements WifiCandidates.CandidateScorer { return choice; } - @Override - public boolean userConnectChoiceOverrideWanted() { - return true; - } - } diff --git a/service/java/com/android/server/wifi/ConfigurationMap.java b/service/java/com/android/server/wifi/ConfigurationMap.java index a5cd18f2a0..02652a81f1 100644 --- a/service/java/com/android/server/wifi/ConfigurationMap.java +++ b/service/java/com/android/server/wifi/ConfigurationMap.java @@ -44,8 +44,10 @@ public class ConfigurationMap { // RW methods: public WifiConfiguration put(WifiConfiguration config) { final WifiConfiguration current = mPerID.put(config.networkId, config); - if (WifiConfigurationUtil.isVisibleToAnyProfile(config, - mUserManager.getProfiles(mCurrentUserId))) { + final UserHandle currentUser = UserHandle.of(mCurrentUserId); + final UserHandle creatorUser = UserHandle.getUserHandleForUid(config.creatorUid); + if (config.shared || currentUser.equals(creatorUser) + || mUserManager.isSameProfileGroup(currentUser, creatorUser)) { mPerIDForCurrentUser.put(config.networkId, config); mScanResultMatchInfoMapForCurrentUser.put( ScanResultMatchInfo.fromWifiConfiguration(config), config); diff --git a/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java index f500c4e56c..35dcd29319 100644 --- a/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java +++ b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java @@ -53,13 +53,16 @@ public class ConnectToNetworkNotificationBuilder { "com.android.server.wifi.ConnectToNetworkNotification.AVAILABLE_NETWORK_NOTIFIER_TAG"; private Context mContext; + private WifiInjector mWifiInjector; private Resources mResources; private FrameworkFacade mFrameworkFacade; public ConnectToNetworkNotificationBuilder( Context context, + WifiInjector wifiInjector, FrameworkFacade framework) { mContext = context; + mWifiInjector = wifiInjector; mResources = context.getResources(); mFrameworkFacade = framework; } @@ -81,9 +84,6 @@ public class ConnectToNetworkNotificationBuilder { case OpenNetworkNotifier.TAG: title = mContext.getText(R.string.wifi_available_title); break; - case CarrierNetworkNotifier.TAG: - title = mContext.getText(R.string.wifi_available_carrier_network_title); - break; default: Log.wtf("ConnectToNetworkNotificationBuilder", "Unknown network notifier." + notifierTag); @@ -153,8 +153,6 @@ public class ConnectToNetworkNotificationBuilder { switch (notifierTag) { case OpenNetworkNotifier.TAG: return 1; - case CarrierNetworkNotifier.TAG: - return 2; } return 0; } @@ -175,7 +173,8 @@ public class ConnectToNetworkNotificationBuilder { } private PendingIntent getPrivateBroadcast(String action, String extraData) { - Intent intent = new Intent(action).setPackage("android"); + Intent intent = new Intent(action) + .setPackage(mWifiInjector.getWifiStackPackageName()); int requestCode = 0; // Makes the different kinds of notifications distinguishable if (extraData != null) { intent.putExtra(AVAILABLE_NETWORK_NOTIFIER_TAG, extraData); diff --git a/service/java/com/android/server/wifi/DefaultModeManager.java b/service/java/com/android/server/wifi/DefaultModeManager.java index a802f491ee..88d657b147 100644 --- a/service/java/com/android/server/wifi/DefaultModeManager.java +++ b/service/java/com/android/server/wifi/DefaultModeManager.java @@ -16,9 +16,7 @@ package com.android.server.wifi; -import android.annotation.NonNull; import android.content.Context; -import android.os.Looper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -55,7 +53,7 @@ public class DefaultModeManager implements ActiveModeManager { */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { } - DefaultModeManager(Context context, @NonNull Looper looper) { + DefaultModeManager(Context context) { mContext = context; } } diff --git a/service/java/com/android/server/wifi/DeviceConfigFacade.java b/service/java/com/android/server/wifi/DeviceConfigFacade.java index a9889f4240..2eeda1e5fd 100644 --- a/service/java/com/android/server/wifi/DeviceConfigFacade.java +++ b/service/java/com/android/server/wifi/DeviceConfigFacade.java @@ -16,93 +16,146 @@ package com.android.server.wifi; +import android.content.Context; +import android.os.Handler; import android.provider.DeviceConfig; -import java.util.concurrent.Executor; +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + import java.util.concurrent.TimeUnit; /** * This class allows getting all configurable flags from DeviceConfig. */ public class DeviceConfigFacade { - private static final int DEFAULT_ABNORMAL_CONNECTION_DURATION_MS = - (int) TimeUnit.SECONDS.toMillis(30); + private Context mContext; + private final WifiMetrics mWifiMetrics; + private static final String NAMESPACE = "wifi"; + + // Default values of fields + @VisibleForTesting + protected static final int DEFAULT_ABNORMAL_CONNECTION_DURATION_MS = + (int) TimeUnit.SECONDS.toMillis(30); // Default duration for evaluating Wifi condition to trigger a data stall // measured in milliseconds public static final int DEFAULT_DATA_STALL_DURATION_MS = 1500; - // Default threshold of Tx throughput below which to trigger a data stall measured in Mbps - public static final int DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS = 2; - // Default threshold of Rx throughput below which to trigger a data stall measured in Mbps - public static final int DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS = 2; + // Default threshold of Tx throughput below which to trigger a data stall measured in Kbps + public static final int DEFAULT_DATA_STALL_TX_TPUT_THR_KBPS = 2000; + // Default threshold of Rx throughput below which to trigger a data stall measured in Kbps + public static final int DEFAULT_DATA_STALL_RX_TPUT_THR_KBPS = 2000; // Default threshold of Tx packet error rate above which to trigger a data stall in percentage public static final int DEFAULT_DATA_STALL_TX_PER_THR = 90; // Default threshold of CCA level above which to trigger a data stall in percentage public static final int DEFAULT_DATA_STALL_CCA_LEVEL_THR = 100; + private boolean mDefaultMacRandomizationAggressiveModeSsidWhitelistEnabled; + + // Cached values of fields updated via updateDeviceConfigFlags() + private boolean mIsAbnormalConnectionBugreportEnabled; + private int mAbnormalConnectionDurationMs; + private boolean mIsAggressiveMacRandomizationSsidWhitelistEnabled; + private int mDataStallDurationMs; + private int mDataStallTxTputThrKbps; + private int mDataStallRxTputThrKbps; + private int mDataStallTxPerThr; + private int mDataStallCcaLevelThr; + + public DeviceConfigFacade(Context context, Handler handler, WifiMetrics wifiMetrics) { + mContext = context; + mWifiMetrics = wifiMetrics; + mDefaultMacRandomizationAggressiveModeSsidWhitelistEnabled = mContext.getResources() + .getBoolean(R.bool.config_wifi_aggressive_randomization_ssid_whitelist_enabled); + + updateDeviceConfigFlags(); + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE, + command -> handler.post(command), + properties -> { + updateDeviceConfigFlags(); + }); + } + + private void updateDeviceConfigFlags() { + mIsAbnormalConnectionBugreportEnabled = DeviceConfig.getBoolean(NAMESPACE, + "abnormal_connection_bugreport_enabled", false); + mAbnormalConnectionDurationMs = DeviceConfig.getInt(NAMESPACE, + "abnormal_connection_duration_ms", + DEFAULT_ABNORMAL_CONNECTION_DURATION_MS); + mIsAggressiveMacRandomizationSsidWhitelistEnabled = DeviceConfig.getBoolean(NAMESPACE, + "aggressive_randomization_ssid_whitelist_enabled", + mDefaultMacRandomizationAggressiveModeSsidWhitelistEnabled); + + mDataStallDurationMs = DeviceConfig.getInt(NAMESPACE, + "data_stall_duration_ms", DEFAULT_DATA_STALL_DURATION_MS); + mDataStallTxTputThrKbps = DeviceConfig.getInt(NAMESPACE, + "data_stall_tx_tput_thr_kbps", DEFAULT_DATA_STALL_TX_TPUT_THR_KBPS); + mDataStallRxTputThrKbps = DeviceConfig.getInt(NAMESPACE, + "data_stall_rx_tput_thr_kbps", DEFAULT_DATA_STALL_RX_TPUT_THR_KBPS); + mDataStallTxPerThr = DeviceConfig.getInt(NAMESPACE, + "data_stall_tx_per_thr", DEFAULT_DATA_STALL_TX_PER_THR); + mDataStallCcaLevelThr = DeviceConfig.getInt(NAMESPACE, + "data_stall_cca_level_thr", DEFAULT_DATA_STALL_CCA_LEVEL_THR); + mWifiMetrics.setDataStallDurationMs(mDataStallDurationMs); + mWifiMetrics.setDataStallTxTputThrKbps(mDataStallTxTputThrKbps); + mWifiMetrics.setDataStallRxTputThrKbps(mDataStallRxTputThrKbps); + mWifiMetrics.setDataStallTxPerThr(mDataStallTxPerThr); + mWifiMetrics.setDataStallCcaLevelThr(mDataStallCcaLevelThr); + } /** * Gets the feature flag for reporting abnormally long connections. */ public boolean isAbnormalConnectionBugreportEnabled() { - return DeviceConfig.getBoolean(NAMESPACE, "abnormal_connection_bugreport_enabled", false); + return mIsAbnormalConnectionBugreportEnabled; } /** * Gets the threshold for classifying abnormally long connections. */ public int getAbnormalConnectionDurationMs() { - return DeviceConfig.getInt(NAMESPACE, "abnormal_connection_duration_ms", - DEFAULT_ABNORMAL_CONNECTION_DURATION_MS); + return mAbnormalConnectionDurationMs; } /** - * Adds a listener that will be run on the specified executor. - * @param executor - * @param onPropertiesChangedListener + * Gets the feature flag for aggressive MAC randomization per-SSID opt-in. */ - public void addOnPropertiesChangedListener(Executor executor, - DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener) { - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor, - onPropertiesChangedListener); + public boolean isAggressiveMacRandomizationSsidWhitelistEnabled() { + return mIsAggressiveMacRandomizationSsidWhitelistEnabled; } /** * Gets the duration of evaluating Wifi condition to trigger a data stall. */ public int getDataStallDurationMs() { - return DeviceConfig.getInt(NAMESPACE, "data_stall_duration_ms", - DEFAULT_DATA_STALL_DURATION_MS); + return mDataStallDurationMs; } /** * Gets the threshold of Tx throughput below which to trigger a data stall. */ - public int getDataStallTxTputThrMbps() { - return DeviceConfig.getInt(NAMESPACE, "data_stall_tx_tput_thr_mbps", - DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS); + public int getDataStallTxTputThrKbps() { + return mDataStallTxTputThrKbps; } /** * Gets the threshold of Rx throughput below which to trigger a data stall. */ - public int getDataStallRxTputThrMbps() { - return DeviceConfig.getInt(NAMESPACE, "data_stall_rx_tput_thr_mbps", - DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS); + public int getDataStallRxTputThrKbps() { + return mDataStallRxTputThrKbps; } /** * Gets the threshold of Tx packet error rate above which to trigger a data stall. */ public int getDataStallTxPerThr() { - return DeviceConfig.getInt(NAMESPACE, "data_stall_tx_per_thr", - DEFAULT_DATA_STALL_TX_PER_THR); + return mDataStallTxPerThr; } /** * Gets the threshold of CCA level above which to trigger a data stall. */ public int getDataStallCcaLevelThr() { - return DeviceConfig.getInt(NAMESPACE, "data_stall_cca_level_thr", - DEFAULT_DATA_STALL_CCA_LEVEL_THR); + return mDataStallCcaLevelThr; } } diff --git a/service/java/com/android/server/wifi/DppManager.java b/service/java/com/android/server/wifi/DppManager.java index e9f77d4f62..e990f9cfb8 100644 --- a/service/java/com/android/server/wifi/DppManager.java +++ b/service/java/com/android/server/wifi/DppManager.java @@ -29,7 +29,6 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -43,7 +42,7 @@ import com.android.server.wifi.WifiNative.DppEventCallback; */ public class DppManager { private static final String TAG = "DppManager"; - public Handler mHandler; + private final Handler mHandler; private DppRequestInfo mDppRequestInfo = null; private final WifiNative mWifiNative; @@ -88,9 +87,9 @@ public class DppManager { } }; - DppManager(Looper looper, WifiNative wifiNative, WifiConfigManager wifiConfigManager, + DppManager(Handler handler, WifiNative wifiNative, WifiConfigManager wifiConfigManager, Context context, DppMetrics dppMetrics) { - mHandler = new Handler(looper); + mHandler = handler; mWifiNative = wifiNative; mWifiConfigManager = wifiConfigManager; mWifiNative.registerDppEventCallback(mDppEventCallback); diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java index f3c5d4b3df..7c636735ae 100644 --- a/service/java/com/android/server/wifi/FrameworkFacade.java +++ b/service/java/com/android/server/wifi/FrameworkFacade.java @@ -16,10 +16,12 @@ package com.android.server.wifi; -import android.app.ActivityManagerInternal; -import android.app.AppGlobals; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; + +import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; @@ -30,14 +32,11 @@ import android.net.ip.IpClientUtil; import android.os.BatteryStats; import android.os.Handler; import android.os.IBinder; -import android.os.RemoteException; import android.os.ServiceManager; -import android.os.storage.StorageManager; import android.provider.Settings; import android.telephony.CarrierConfigManager; import com.android.internal.app.IBatteryStats; -import com.android.server.LocalServices; import com.android.server.wifi.util.WifiAsyncChannel; /** @@ -46,40 +45,65 @@ import com.android.server.wifi.util.WifiAsyncChannel; public class FrameworkFacade { public static final String TAG = "FrameworkFacade"; - private ActivityManagerInternal mActivityManagerInternal; + private ContentResolver mContentResolver = null; + private CarrierConfigManager mCarrierConfigManager = null; + private ActivityManager mActivityManager = null; + + private ContentResolver getContentResolver(Context context) { + if (mContentResolver == null) { + mContentResolver = context.getContentResolver(); + } + return mContentResolver; + } + + private CarrierConfigManager getCarrierConfigManager(Context context) { + if (mCarrierConfigManager == null) { + mCarrierConfigManager = + (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); + } + return mCarrierConfigManager; + } + + private ActivityManager getActivityManager(Context context) { + if (mActivityManager == null) { + mActivityManager = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + } + return mActivityManager; + } public boolean setIntegerSetting(Context context, String name, int def) { - return Settings.Global.putInt(context.getContentResolver(), name, def); + return Settings.Global.putInt(getContentResolver(context), name, def); } public int getIntegerSetting(Context context, String name, int def) { - return Settings.Global.getInt(context.getContentResolver(), name, def); + return Settings.Global.getInt(getContentResolver(context), name, def); } public long getLongSetting(Context context, String name, long def) { - return Settings.Global.getLong(context.getContentResolver(), name, def); + return Settings.Global.getLong(getContentResolver(context), name, def); } public boolean setStringSetting(Context context, String name, String def) { - return Settings.Global.putString(context.getContentResolver(), name, def); + return Settings.Global.putString(getContentResolver(context), name, def); } public String getStringSetting(Context context, String name) { - return Settings.Global.getString(context.getContentResolver(), name); + return Settings.Global.getString(getContentResolver(context), name); } /** * Mockable facade to Settings.Secure.getInt(.). */ public int getSecureIntegerSetting(Context context, String name, int def) { - return Settings.Secure.getInt(context.getContentResolver(), name, def); + return Settings.Secure.getInt(getContentResolver(context), name, def); } /** * Mockable facade to Settings.Secure.getString(.). */ public String getSecureStringSetting(Context context, String name) { - return Settings.Secure.getString(context.getContentResolver(), name); + return Settings.Secure.getString(getContentResolver(context), name); } /** @@ -93,7 +117,7 @@ public class FrameworkFacade { */ public void registerContentObserver(Context context, Uri uri, boolean notifyForDescendants, ContentObserver contentObserver) { - context.getContentResolver().registerContentObserver(uri, notifyForDescendants, + getContentResolver(context).registerContentObserver(uri, notifyForDescendants, contentObserver); } @@ -105,7 +129,7 @@ public class FrameworkFacade { * @param contentObserver */ public void unregisterContentObserver(Context context, ContentObserver contentObserver) { - context.getContentResolver().unregisterContentObserver(contentObserver); + getContentResolver(context).unregisterContentObserver(contentObserver); } public IBinder getService(String serviceName) { @@ -137,8 +161,7 @@ public class FrameworkFacade { } public boolean getConfigWiFiDisableInECBM(Context context) { - CarrierConfigManager configManager = (CarrierConfigManager) context - .getSystemService(Context.CARRIER_CONFIG_SERVICE); + CarrierConfigManager configManager = getCarrierConfigManager(context); if (configManager != null) { return configManager.getConfig().getBoolean( CarrierConfigManager.KEY_CONFIG_WIFI_DISABLE_IN_ECBM); @@ -166,17 +189,6 @@ public class FrameworkFacade { } /** - * Checks whether the given uid has been granted the given permission. - * @param permName the permission to check - * @param uid The uid to check - * @return {@link PackageManager.PERMISSION_GRANTED} if the permission has been granted and - * {@link PackageManager.PERMISSION_DENIED} otherwise - */ - public int checkUidPermission(String permName, int uid) throws RemoteException { - return AppGlobals.getPackageManager().checkUidPermission(permName, uid); - } - - /** * Create a new instance of WifiAsyncChannel * @param tag String corresponding to the service creating the channel * @return WifiAsyncChannel object created @@ -186,24 +198,14 @@ public class FrameworkFacade { } /** - * 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 */ - public boolean isAppForeground(int uid) { - if (mActivityManagerInternal == null) { - mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); - } - return mActivityManagerInternal.isAppForeground(uid); + public boolean isAppForeground(Context context, int uid) { + ActivityManager activityManager = getActivityManager(context); + if (activityManager == null) return false; + return activityManager.getUidImportance(uid) <= IMPORTANCE_VISIBLE; } /** diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java index e10234f68d..a51a78513e 100644 --- a/service/java/com/android/server/wifi/HalDeviceManager.java +++ b/service/java/com/android/server/wifi/HalDeviceManager.java @@ -35,9 +35,7 @@ import android.hardware.wifi.V1_0.WifiStatusCode; import android.hidl.manager.V1_0.IServiceNotification; import android.hidl.manager.V1_2.IServiceManager; import android.os.Handler; -import android.os.HidlSupport.Mutable; import android.os.HwRemoteBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.util.LongSparseArray; @@ -47,6 +45,7 @@ import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.util.GeneralUtil.Mutable; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -82,9 +81,9 @@ public class HalDeviceManager { private boolean mIsVendorHalSupported = false; // public API - public HalDeviceManager(Clock clock, Looper looper) { + public HalDeviceManager(Clock clock, Handler handler) { mClock = clock; - mEventHandler = new Handler(looper); + mEventHandler = handler; mIWifiDeathRecipient = new WifiDeathRecipient(); mServiceManagerDeathRecipient = new ServiceManagerDeathRecipient(); diff --git a/service/java/com/android/server/wifi/HostapdHal.java b/service/java/com/android/server/wifi/HostapdHal.java index 5ac173447d..35f8021583 100644 --- a/service/java/com/android/server/wifi/HostapdHal.java +++ b/service/java/com/android/server/wifi/HostapdHal.java @@ -26,7 +26,6 @@ import android.hidl.manager.V1_0.IServiceNotification; import android.net.wifi.WifiConfiguration; import android.os.Handler; import android.os.HwRemoteBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -119,8 +118,8 @@ public class HostapdHal { } } - public HostapdHal(Context context, Looper looper) { - mEventHandler = new Handler(looper); + public HostapdHal(Context context, Handler handler) { + mEventHandler = handler; mEnableAcs = context.getResources().getBoolean(R.bool.config_wifi_softap_acs_supported); mEnableIeee80211AC = context.getResources().getBoolean(R.bool.config_wifi_softap_ieee80211ac_supported); diff --git a/service/java/com/android/server/wifi/LastMileLogger.java b/service/java/com/android/server/wifi/LastMileLogger.java index 1269638b36..cc0685a470 100644 --- a/service/java/com/android/server/wifi/LastMileLogger.java +++ b/service/java/com/android/server/wifi/LastMileLogger.java @@ -16,9 +16,9 @@ package com.android.server.wifi; -import android.os.FileUtils; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.util.FileUtils; import libcore.io.IoUtils; diff --git a/service/java/com/android/server/wifi/LinkProbeManager.java b/service/java/com/android/server/wifi/LinkProbeManager.java index 89c84f76f1..b33b9d3896 100644 --- a/service/java/com/android/server/wifi/LinkProbeManager.java +++ b/service/java/com/android/server/wifi/LinkProbeManager.java @@ -21,7 +21,6 @@ import android.database.ContentObserver; import android.net.MacAddress; import android.net.wifi.WifiInfo; import android.os.Handler; -import android.os.Looper; import android.provider.Settings; import android.util.Log; @@ -105,7 +104,7 @@ public class LinkProbeManager { private final TimedQuotaManager mTimedQuotaManager; public LinkProbeManager(Clock clock, WifiNative wifiNative, WifiMetrics wifiMetrics, - FrameworkFacade frameworkFacade, Looper looper, Context context) { + FrameworkFacade frameworkFacade, Handler handler, Context context) { mClock = clock; mWifiNative = wifiNative; mWifiMetrics = wifiMetrics; @@ -118,7 +117,7 @@ public class LinkProbeManager { if (mLinkProbingSupported) { mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor( Settings.Global.WIFI_LINK_PROBING_ENABLED), false, - new ContentObserver(new Handler(looper)) { + new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { updateLinkProbeSetting(); diff --git a/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java index 9e3b4fb2ce..2226518753 100644 --- a/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java +++ b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java @@ -17,12 +17,10 @@ package com.android.server.wifi; import android.annotation.NonNull; +import android.net.wifi.ILocalOnlyHotspotCallback; 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; @@ -36,9 +34,8 @@ 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; + private final ILocalOnlyHotspotCallback mCallback; + private final RequestingApplicationDeathCallback mDeathCallback; /** * Callback for use with LocalOnlyHotspot to unregister requesting applications upon death. @@ -50,15 +47,14 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { void onLocalOnlyHotspotRequestorDeath(LocalOnlyHotspotRequestInfo requestor); } - LocalOnlyHotspotRequestInfo(@NonNull IBinder binder, @NonNull Messenger messenger, - @NonNull RequestingApplicationDeathCallback callback) { + LocalOnlyHotspotRequestInfo(@NonNull ILocalOnlyHotspotCallback callback, + @NonNull RequestingApplicationDeathCallback deathCallback) { mPid = Binder.getCallingPid(); - mBinder = Preconditions.checkNotNull(binder); - mMessenger = Preconditions.checkNotNull(messenger); mCallback = Preconditions.checkNotNull(callback); + mDeathCallback = Preconditions.checkNotNull(deathCallback); try { - mBinder.linkToDeath(this, 0); + mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { binderDied(); } @@ -68,7 +64,7 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { * Allow caller to unlink this object from binder death. */ public void unlinkDeathRecipient() { - mBinder.unlinkToDeath(this, 0); + mCallback.asBinder().unlinkToDeath(this, 0); } /** @@ -76,7 +72,7 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { */ @Override public void binderDied() { - mCallback.onLocalOnlyHotspotRequestorDeath(this); + mDeathCallback.onLocalOnlyHotspotRequestorDeath(this); } /** @@ -87,10 +83,7 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { * @throws RemoteException */ public void sendHotspotFailedMessage(int reasonCode) throws RemoteException { - Message message = Message.obtain(); - message.what = WifiManager.HOTSPOT_FAILED; - message.arg1 = reasonCode; - mMessenger.send(message); + mCallback.onHotspotFailed(reasonCode); } /** @@ -101,10 +94,7 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { * @throws RemoteException */ public void sendHotspotStartedMessage(WifiConfiguration config) throws RemoteException { - Message message = Message.obtain(); - message.what = WifiManager.HOTSPOT_STARTED; - message.obj = config; - mMessenger.send(message); + mCallback.onHotspotStarted(config); } /** @@ -113,9 +103,7 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { * @throws RemoteException */ public void sendHotspotStoppedMessage() throws RemoteException { - Message message = Message.obtain(); - message.what = WifiManager.HOTSPOT_STOPPED; - mMessenger.send(message); + mCallback.onHotspotStopped(); } public int getPid() { diff --git a/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java b/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java index ddc0b71a7e..690cd97299 100644 --- a/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java +++ b/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java @@ -102,11 +102,25 @@ public class NetworkSuggestionEvaluator implements WifiNetworkSelector.NetworkEv WifiNetworkSuggestion matchingNetworkSuggestion = matchingNetworkSuggestions.stream().findAny().get(); // Check if we already have a network with the same credentials in WifiConfigManager - // database. If yes, we should check if the network is currently blacklisted. + // database. WifiConfiguration wCmConfiguredNetwork = mWifiConfigManager.getConfiguredNetwork( matchingNetworkSuggestion.wifiConfiguration.configKey()); if (wCmConfiguredNetwork != null) { + // If existing network is not from suggestion, ignore. + if (!wCmConfiguredNetwork.fromWifiNetworkSuggestion) { + continue; + } + // Update the WifiConfigManager with the latest WifiConfig + NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork( + matchingNetworkSuggestion.wifiConfiguration, + matchingNetworkSuggestion.suggestorUid, + matchingNetworkSuggestion.suggestorPackageName); + if (result.isSuccess()) { + wCmConfiguredNetwork = mWifiConfigManager.getConfiguredNetwork( + result.getNetworkId()); + } + // If the network is currently blacklisted, ignore. if (!wCmConfiguredNetwork.getNetworkSelectionStatus().isNetworkEnabled() && !mWifiConfigManager.tryEnableNetwork(wCmConfiguredNetwork.networkId)) { mLocalLog.log("Ignoring blacklisted network: " diff --git a/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java b/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java index 9627a9daaa..5babd3830c 100644 --- a/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java +++ b/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java @@ -19,6 +19,7 @@ package com.android.server.wifi; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiNetworkSuggestion; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Process; import android.util.Log; import android.util.Pair; @@ -26,6 +27,7 @@ import android.util.Pair; import com.android.internal.util.XmlUtils; import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion; import com.android.server.wifi.WifiNetworkSuggestionsManager.PerAppInfo; +import com.android.server.wifi.hotspot2.PasspointXmlUtils; import com.android.server.wifi.util.XmlUtil; import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil; @@ -61,6 +63,8 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData { private static final String XML_TAG_SUGGESTOR_PACKAGE_NAME = "SuggestorPackageName"; private static final String XML_TAG_SUGGESTOR_HAS_USER_APPROVED = "SuggestorHasUserApproved"; private static final String XML_TAG_SUGGESTOR_MAX_SIZE = "SuggestorMaxSize"; + private static final String XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION = + "PasspointConfiguration"; /** * Interface define the data source for the network suggestions store data. @@ -199,6 +203,12 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData { out, suggestion.wifiConfiguration.enterpriseConfig); XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION); } + if (suggestion.passpointConfiguration != null) { + XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION); + PasspointXmlUtils.serializePasspointConfiguration(out, + suggestion.passpointConfiguration); + XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION); + } // Serialize other fields XmlUtil.writeNextValue(out, XML_TAG_IS_APP_INTERACTION_REQUIRED, @@ -283,6 +293,7 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData { throws XmlPullParserException, IOException { Pair<String, WifiConfiguration> parsedConfig = null; WifiEnterpriseConfig enterpriseConfig = null; + PasspointConfiguration passpointConfiguration = null; boolean isAppInteractionRequired = false; boolean isUserInteractionRequired = false; int suggestorUid = Process.INVALID_UID; @@ -334,6 +345,14 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData { enterpriseConfig = XmlUtil.WifiEnterpriseConfigXmlUtil.parseFromXml( in, outerTagDepth + 1); break; + case XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION: + if (passpointConfiguration != null) { + throw new XmlPullParserException("Detected duplicate tag for: " + + XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION); + } + passpointConfiguration = PasspointXmlUtils + .deserializePasspointConfiguration(in, outerTagDepth + 1); + break; default: throw new XmlPullParserException("Unknown tag under " + XML_TAG_SECTION_HEADER_NETWORK_SUGGESTION + ": " + in.getName()); @@ -354,8 +373,8 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData { wifiConfiguration.enterpriseConfig = enterpriseConfig; } return new WifiNetworkSuggestion( - wifiConfiguration, isAppInteractionRequired, isUserInteractionRequired, - suggestorUid, suggestorPackageName); + wifiConfiguration, passpointConfiguration, isAppInteractionRequired, + isUserInteractionRequired, suggestorUid, suggestorPackageName); } } diff --git a/service/java/com/android/server/wifi/OpenNetworkNotifier.java b/service/java/com/android/server/wifi/OpenNetworkNotifier.java index 97f390f391..dffee2dc25 100644 --- a/service/java/com/android/server/wifi/OpenNetworkNotifier.java +++ b/service/java/com/android/server/wifi/OpenNetworkNotifier.java @@ -26,7 +26,7 @@ import com.android.server.wifi.nano.WifiMetricsProto; /** * This class handles the "open wi-fi network available" notification * - * NOTE: These API's are not thread safe and should only be used from ClientModeImpl thread. + * NOTE: These API's are not thread safe and should only be used from the main Wifi thread. */ public class OpenNetworkNotifier extends AvailableNetworkNotifier { public static final String TAG = "WifiOpenNetworkNotifier"; diff --git a/service/java/com/android/server/wifi/SIMAccessor.java b/service/java/com/android/server/wifi/SIMAccessor.java index efa303752d..13186f1e1e 100644 --- a/service/java/com/android/server/wifi/SIMAccessor.java +++ b/service/java/com/android/server/wifi/SIMAccessor.java @@ -1,6 +1,7 @@ package com.android.server.wifi; import android.content.Context; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -13,9 +14,9 @@ public class SIMAccessor { public SIMAccessor(Context context) { // TODO(b/132188983): Inject this using WifiInjector - mTelephonyManager = TelephonyManager.from(context); + mTelephonyManager = context.getSystemService(TelephonyManager.class); // TODO(b/132188983): Inject this using WifiInjector - mSubscriptionManager = SubscriptionManager.from(context); + mSubscriptionManager = context.getSystemService(SubscriptionManager.class); } public List<String> getMatchingImsis(IMSIParameter mccMnc) { @@ -23,8 +24,10 @@ public class SIMAccessor { return null; } List<String> imsis = new ArrayList<>(); - for (int subId : mSubscriptionManager.getActiveSubscriptionIdList()) { - String imsi = mTelephonyManager.getSubscriberId(subId); + for (SubscriptionInfo sub : mSubscriptionManager.getActiveSubscriptionInfoList()) { + String imsi = + mTelephonyManager.createForSubscriptionId(sub.getSubscriptionId()) + .getSubscriberId(); if (imsi != null && mccMnc.matchesImsi(imsi)) { imsis.add(imsi); } diff --git a/service/java/com/android/server/wifi/SarManager.java b/service/java/com/android/server/wifi/SarManager.java index a35f65f00b..ebc414f7f2 100644 --- a/service/java/com/android/server/wifi/SarManager.java +++ b/service/java/com/android/server/wifi/SarManager.java @@ -32,6 +32,7 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.net.wifi.WifiManager; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; @@ -486,7 +487,7 @@ public class SarManager { */ private class WifiPhoneStateListener extends PhoneStateListener { WifiPhoneStateListener(Looper looper) { - super(looper); + super(new HandlerExecutor(new Handler(looper))); } /** diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java index 062fa5354a..d12b76a6fb 100644 --- a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java @@ -205,6 +205,11 @@ public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluat continue; } + // Ignore networks that the user has disallowed auto-join for. + if (!network.allowAutojoin) { + continue; + } + /** * Ignore Passpoint and Ephemeral networks. They are configured networks, * but without being persisted to the storage. They are evaluated by diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java index d39888ec45..0ee6cc4885 100644 --- a/service/java/com/android/server/wifi/ScanDetail.java +++ b/service/java/com/android/server/wifi/ScanDetail.java @@ -20,13 +20,13 @@ import android.net.wifi.AnqpInformationElement; import android.net.wifi.ScanResult; import android.net.wifi.WifiSsid; +import com.android.server.wifi.hotspot2.NetworkDetail; +import com.android.server.wifi.hotspot2.Utils; 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.Utils; import java.util.List; import java.util.Map; @@ -38,10 +38,12 @@ public class ScanDetail { private final ScanResult mScanResult; private volatile NetworkDetail mNetworkDetail; private long mSeen = 0; + private byte[] mInformationElementRawData; public ScanDetail(NetworkDetail networkDetail, WifiSsid wifiSsid, String bssid, String caps, int level, int frequency, long tsf, - ScanResult.InformationElement[] informationElements, List<String> anqpLines) { + ScanResult.InformationElement[] informationElements, List<String> anqpLines, + byte[] informationElementRawData) { mNetworkDetail = networkDetail; mScanResult = new ScanResult(wifiSsid, bssid, networkDetail.getHESSID(), networkDetail.getAnqpDomainID(), networkDetail.getOsuProviders(), @@ -59,6 +61,7 @@ public class ScanDetail { if (networkDetail.isInterworking()) { mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK); } + mInformationElementRawData = informationElementRawData; } public ScanDetail(WifiSsid wifiSsid, String bssid, String caps, int level, int frequency, @@ -160,6 +163,13 @@ public class ScanDetail { return mSeen; } + /** + * Return the network information element raw data. + */ + public byte[] getInformationElementRawData() { + return mInformationElementRawData; + } + @Override public String toString() { try { diff --git a/service/java/com/android/server/wifi/ScanOnlyModeManager.java b/service/java/com/android/server/wifi/ScanOnlyModeManager.java deleted file mode 100644 index c3547be5de..0000000000 --- a/service/java/com/android/server/wifi/ScanOnlyModeManager.java +++ /dev/null @@ -1,286 +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.annotation.NonNull; -import android.content.Context; -import android.net.wifi.WifiManager; -import android.os.Looper; -import android.os.Message; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.util.IState; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.server.wifi.WifiNative.InterfaceCallback; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** - * Manager WiFi in Scan Only Mode - no network connections. - */ -public class ScanOnlyModeManager implements ActiveModeManager { - - private final ScanOnlyModeStateMachine mStateMachine; - - private static final String TAG = "WifiScanOnlyModeManager"; - - private final Context mContext; - private final WifiNative mWifiNative; - - private final WifiMetrics mWifiMetrics; - private final Listener mListener; - private final WakeupController mWakeupController; - private final SarManager mSarManager; - - private String mClientInterfaceName; - private boolean mIfaceIsUp = false; - - private boolean mExpectedStop = false; - - ScanOnlyModeManager(@NonNull Context context, @NonNull Looper looper, - @NonNull WifiNative wifiNative, @NonNull Listener listener, - @NonNull WifiMetrics wifiMetrics, - @NonNull WakeupController wakeupController, - @NonNull SarManager sarManager) { - mContext = context; - mWifiNative = wifiNative; - mListener = listener; - mWifiMetrics = wifiMetrics; - mWakeupController = wakeupController; - mSarManager = sarManager; - mStateMachine = new ScanOnlyModeStateMachine(looper); - } - - /** - * Start scan only mode. - */ - public void start() { - mStateMachine.sendMessage(ScanOnlyModeStateMachine.CMD_START); - } - - /** - * Cancel any pending scans and stop scan mode. - */ - public void stop() { - Log.d(TAG, " currentstate: " + getCurrentStateName()); - mExpectedStop = true; - mStateMachine.quitNow(); - } - - public @ScanMode int getScanMode() { - return SCAN_WITHOUT_HIDDEN_NETWORKS; - } - - /** - * Dump info about this ScanOnlyMode manager. - */ - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("--Dump of ScanOnlyModeManager--"); - - pw.println("current StateMachine mode: " + getCurrentStateName()); - pw.println("mClientInterfaceName: " + mClientInterfaceName); - pw.println("mIfaceIsUp: " + mIfaceIsUp); - } - - /** - * Listener for ScanOnlyMode state changes. - */ - public interface Listener { - /** - * Invoke when wifi state changes. - * @param state new wifi state - */ - void onStateChanged(int state); - } - - private String getCurrentStateName() { - IState currentState = mStateMachine.getCurrentState(); - - if (currentState != null) { - return currentState.getName(); - } - - return "StateMachine not active"; - } - - /** - * Update Wifi state. - * @param state new Wifi state - */ - private void updateWifiState(int state) { - if (mExpectedStop) { - Log.d(TAG, "expected stop, not triggering callbacks: state = " + state); - return; - } - - // Once we report the mode has stopped/failed any other stop signals are redundant - // note: this can happen in failure modes where we get multiple callbacks as underlying - // components/interface stops or the underlying interface is destroyed in cleanup - if (state == WifiManager.WIFI_STATE_UNKNOWN || state == WifiManager.WIFI_STATE_DISABLED) { - mExpectedStop = true; - } - - mListener.onStateChanged(state); - } - - private class ScanOnlyModeStateMachine extends StateMachine { - // Commands for the state machine. - public static final int CMD_START = 0; - public static final int CMD_INTERFACE_STATUS_CHANGED = 3; - public static final int CMD_INTERFACE_DESTROYED = 4; - public static final int CMD_INTERFACE_DOWN = 5; - - private final State mIdleState = new IdleState(); - private final State mStartedState = new StartedState(); - - private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { - @Override - public void onDestroyed(String ifaceName) { - if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { - sendMessage(CMD_INTERFACE_DESTROYED); - } - } - - @Override - public void onUp(String ifaceName) { - if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { - sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); - } - } - - @Override - public void onDown(String ifaceName) { - if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { - sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); - } - } - }; - - ScanOnlyModeStateMachine(Looper looper) { - super(TAG, looper); - - addState(mIdleState); - addState(mStartedState); - - setInitialState(mIdleState); - start(); - } - - private class IdleState extends State { - - @Override - public void enter() { - Log.d(TAG, "entering IdleState"); - mClientInterfaceName = null; - } - - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_START: - mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode( - mWifiNativeInterfaceCallback); - if (TextUtils.isEmpty(mClientInterfaceName)) { - Log.e(TAG, "Failed to create ClientInterface. Sit in Idle"); - updateWifiState(WifiManager.WIFI_STATE_UNKNOWN); - break; - } - transitionTo(mStartedState); - break; - default: - Log.d(TAG, "received an invalid message: " + message); - return NOT_HANDLED; - } - return HANDLED; - } - } - - private class StartedState extends State { - - private void onUpChanged(boolean isUp) { - if (isUp == mIfaceIsUp) { - return; // no change - } - mIfaceIsUp = isUp; - if (isUp) { - Log.d(TAG, "Wifi is ready to use for scanning"); - mWakeupController.start(); - updateWifiState(WifiManager.WIFI_STATE_ENABLED); - } else { - // if the interface goes down we should exit and go back to idle state. - Log.d(TAG, "interface down - stop scan mode"); - mStateMachine.sendMessage(CMD_INTERFACE_DOWN); - } - } - - @Override - public void enter() { - Log.d(TAG, "entering StartedState"); - - mIfaceIsUp = false; - onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName)); - mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); - } - - @Override - public boolean processMessage(Message message) { - switch(message.what) { - case CMD_START: - // Already started, ignore this command. - break; - case CMD_INTERFACE_DESTROYED: - Log.d(TAG, "Interface cleanly destroyed, report scan mode stop."); - mClientInterfaceName = null; - transitionTo(mIdleState); - break; - case CMD_INTERFACE_STATUS_CHANGED: - boolean isUp = message.arg1 == 1; - onUpChanged(isUp); - break; - case CMD_INTERFACE_DOWN: - Log.d(TAG, "interface down! stop mode"); - updateWifiState(WifiManager.WIFI_STATE_UNKNOWN); - transitionTo(mIdleState); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - - /** - * Clean up state and unregister listeners. - */ - @Override - public void exit() { - mWakeupController.stop(); - if (mClientInterfaceName != null) { - mWifiNative.teardownInterface(mClientInterfaceName); - mClientInterfaceName = null; - } - updateWifiState(WifiManager.WIFI_STATE_DISABLED); - mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_DISABLED); - - // once we leave started, nothing else to do... stop the state machine - mStateMachine.quitNow(); - } - } - } -} diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java index a4678440b2..9ba112536d 100644 --- a/service/java/com/android/server/wifi/ScanRequestProxy.java +++ b/service/java/com/android/server/wifi/ScanRequestProxy.java @@ -22,10 +22,13 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; +import android.net.wifi.IScanResultsListener; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; @@ -34,6 +37,7 @@ import android.util.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.util.ExternalCallbackTracker; import com.android.server.wifi.util.WifiPermissionsUtil; import java.util.ArrayList; @@ -63,7 +67,7 @@ import javax.annotation.concurrent.NotThreadSafe; * {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS}. * b) Background apps combined can request 1 scan every * {@link #SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}. - * Note: This class is not thread-safe. It needs to be invoked from ClientModeImpl thread only. + * Note: This class is not thread-safe. It needs to be invoked from the main Wifi thread only. */ @NotThreadSafe public class ScanRequestProxy { @@ -103,6 +107,8 @@ public class ScanRequestProxy { new ArrayMap(); // Scan results cached from the last full single scan request. private final List<ScanResult> mLastScanResults = new ArrayList<>(); + // external ScanResultListener tracker + private final ExternalCallbackTracker<IScanResultsListener> mRegisteredScanResultsListeners; // Global scan listener for listening to all scan requests. private class GlobalScanListener implements WifiScanner.ScanListener { @Override @@ -137,6 +143,7 @@ public class ScanRequestProxy { mLastScanResults.clear(); mLastScanResults.addAll(Arrays.asList(scanResults)); sendScanResultBroadcast(true); + sendScanResultsAvailableToListeners(); } } @@ -244,6 +251,7 @@ public class ScanRequestProxy { mClock = clock; mFrameworkFacade = frameworkFacade; mThrottleEnabledSettingObserver = new ThrottleEnabledSettingObserver(handler); + mRegisteredScanResultsListeners = new ExternalCallbackTracker<>(handler); } /** @@ -453,7 +461,7 @@ public class ScanRequestProxy { * @return true if the scan request was placed or a scan is already ongoing, false otherwise. */ public boolean startScan(int callingUid, String packageName) { - if (!retrieveWifiScannerIfNecessary()) { + if (!mScanningEnabled || !retrieveWifiScannerIfNecessary()) { Log.e(TAG, "Failed to retrieve wifiscanner"); sendScanResultFailureBroadcastToPackage(packageName); return false; @@ -528,4 +536,37 @@ public class ScanRequestProxy { } mLastScanTimestampsForFgApps.remove(Pair.create(uid, packageName)); } + + private void sendScanResultsAvailableToListeners() { + Iterator<IScanResultsListener> iterator = + mRegisteredScanResultsListeners.getCallbacks().iterator(); + while (iterator.hasNext()) { + IScanResultsListener listener = iterator.next(); + try { + listener.onScanResultsAvailable(); + } catch (RemoteException e) { + Log.e(TAG, "onScanResultsAvailable: remote exception -- " + e); + } + } + } + + /** + * Register a listener on scan event + * @param binder IBinder instance to allow cleanup if the app dies. + * @param listener IScanResultListener instance to add. + * @param listenerIdentifier identifier of the listener, should be hash code of package name. + * @return true if succeed otherwise false. + */ + public boolean registerScanResultsListener(IBinder binder, IScanResultsListener listener, + int listenerIdentifier) { + return mRegisteredScanResultsListeners.add(binder, listener, listenerIdentifier); + } + + /** + * Unregister a listener on scan event + * @param listenerIdentifier identifier of the listener, should be hash code of package name. + */ + public void unregisterScanResultsListener(int listenerIdentifier) { + mRegisteredScanResultsListeners.remove(listenerIdentifier); + } } diff --git a/service/java/com/android/server/wifi/ScanResultMatchInfo.java b/service/java/com/android/server/wifi/ScanResultMatchInfo.java index b3d10cc386..e623683318 100644 --- a/service/java/com/android/server/wifi/ScanResultMatchInfo.java +++ b/service/java/com/android/server/wifi/ScanResultMatchInfo.java @@ -15,6 +15,7 @@ */ package com.android.server.wifi; +import android.annotation.NonNull; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; @@ -46,7 +47,7 @@ public class ScanResultMatchInfo { /** * Fetch network type from network configuration. */ - public static @WifiConfiguration.SecurityType int getNetworkType(WifiConfiguration config) { + private static @WifiConfiguration.SecurityType int getNetworkType(WifiConfiguration config) { if (WifiConfigurationUtil.isConfigForSaeNetwork(config)) { return WifiConfiguration.SECURITY_TYPE_SAE; } else if (WifiConfigurationUtil.isConfigForPskNetwork(config)) { @@ -78,7 +79,7 @@ public class ScanResultMatchInfo { /** * Fetch network type from scan result. */ - public static @WifiConfiguration.SecurityType int getNetworkType(ScanResult scanResult) { + private static @WifiConfiguration.SecurityType int getNetworkType(ScanResult scanResult) { if (ScanResultUtil.isScanResultForSaeNetwork(scanResult)) { return WifiConfiguration.SECURITY_TYPE_SAE; } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) { @@ -123,19 +124,11 @@ public class ScanResultMatchInfo { return info; } - @Override - public boolean equals(Object otherObj) { - if (this == otherObj) { - return true; - } else if (!(otherObj instanceof ScanResultMatchInfo)) { - return false; - } - ScanResultMatchInfo other = (ScanResultMatchInfo) otherObj; - if (!Objects.equals(networkSsid, other.networkSsid)) { - return false; - } + /** + * Checks for equality of network type. + */ + public boolean networkTypeEquals(@NonNull ScanResultMatchInfo other) { boolean networkTypeEquals; - // Detect <SSID, PSK+SAE> scan result and say it is equal to <SSID, PSK> configuration if (other.pskSaeInTransitionMode && networkType == WifiConfiguration.SECURITY_TYPE_PSK || (pskSaeInTransitionMode @@ -154,6 +147,20 @@ public class ScanResultMatchInfo { } @Override + public boolean equals(Object otherObj) { + if (this == otherObj) { + return true; + } else if (!(otherObj instanceof ScanResultMatchInfo)) { + return false; + } + ScanResultMatchInfo other = (ScanResultMatchInfo) otherObj; + if (!Objects.equals(networkSsid, other.networkSsid)) { + return false; + } + return networkTypeEquals(other); + } + + @Override public int hashCode() { return Objects.hash(networkSsid); } diff --git a/service/java/com/android/server/wifi/ScoreCardBasedScorer.java b/service/java/com/android/server/wifi/ScoreCardBasedScorer.java index 34adfcba47..96c8e6c099 100644 --- a/service/java/com/android/server/wifi/ScoreCardBasedScorer.java +++ b/service/java/com/android/server/wifi/ScoreCardBasedScorer.java @@ -64,6 +64,8 @@ final class ScoreCardBasedScorer implements WifiCandidates.CandidateScorer { // Maximum allowable adjustment of the cutoff rssi (dB) public static final int RSSI_RAIL = 5; + private static final boolean USE_USER_CONNECT_CHOICE = true; + ScoreCardBasedScorer(ScoringParams scoringParams) { mScoringParams = scoringParams; } @@ -99,7 +101,8 @@ final class ScoreCardBasedScorer implements WifiCandidates.CandidateScorer { // which evaluator added the candidate. score -= 1000 * candidate.getEvaluatorId(); - return new ScoredCandidate(score, 10, candidate); + return new ScoredCandidate(score, 10, + USE_USER_CONNECT_CHOICE, candidate); } private int estimatedCutoff(Candidate candidate) { @@ -121,9 +124,9 @@ final class ScoreCardBasedScorer implements WifiCandidates.CandidateScorer { } @Override - public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> group) { + public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> candidates) { ScoredCandidate choice = ScoredCandidate.NONE; - for (Candidate candidate : group) { + for (Candidate candidate : candidates) { ScoredCandidate scoredCandidate = scoreCandidate(candidate); if (scoredCandidate.value > choice.value) { choice = scoredCandidate; @@ -134,9 +137,4 @@ final class ScoreCardBasedScorer implements WifiCandidates.CandidateScorer { return choice; } - @Override - public boolean userConnectChoiceOverrideWanted() { - return true; - } - } diff --git a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java index 66222691fb..2eba641997 100644 --- a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java @@ -27,14 +27,12 @@ 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 com.android.server.wifi.WifiNetworkSelector.NetworkEvaluator.OnConnectableListener; import com.android.server.wifi.util.ScanResultUtil; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -57,7 +55,7 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua private boolean mNetworkRecommendationsEnabled; private WifiNetworkScoreCache mScoreCache; - ScoredNetworkEvaluator(final Context context, Looper looper, + ScoredNetworkEvaluator(final Context context, Handler handler, final FrameworkFacade frameworkFacade, NetworkScoreManager networkScoreManager, WifiConfigManager wifiConfigManager, LocalLog localLog, WifiNetworkScoreCache wifiNetworkScoreCache, @@ -67,7 +65,7 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua mNetworkScoreManager = networkScoreManager; mWifiConfigManager = wifiConfigManager; mLocalLog = localLog; - mContentObserver = new ContentObserver(new Handler(looper)) { + mContentObserver = new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { mNetworkRecommendationsEnabled = frameworkFacade.getIntegerSetting(context, diff --git a/service/java/com/android/server/wifi/SelfRecovery.java b/service/java/com/android/server/wifi/SelfRecovery.java index c9e95a77fd..e789dd3a4a 100644 --- a/service/java/com/android/server/wifi/SelfRecovery.java +++ b/service/java/com/android/server/wifi/SelfRecovery.java @@ -16,15 +16,18 @@ package com.android.server.wifi; +import android.annotation.IntDef; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Iterator; import java.util.LinkedList; /** * This class is used to recover the wifi stack from a fatal failure. The recovery mechanism * involves triggering a stack restart (essentially simulating an airplane mode toggle) using - * {@link WifiController}. + * {@link ActiveModeWarden}. * The current triggers for: * 1. Last resort watchdog bite. * 2. HAL/wificond crashes during normal operation. @@ -39,6 +42,14 @@ public class SelfRecovery { public static final int REASON_LAST_RESORT_WATCHDOG = 0; public static final int REASON_WIFINATIVE_FAILURE = 1; public static final int REASON_STA_IFACE_DOWN = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"REASON_"}, value = { + REASON_LAST_RESORT_WATCHDOG, + REASON_WIFINATIVE_FAILURE, + REASON_STA_IFACE_DOWN}) + public @interface RecoveryReason {} + public static final long MAX_RESTARTS_IN_TIME_WINDOW = 2; // 2 restarts per hour public static final long MAX_RESTARTS_TIME_WINDOW_MILLIS = 60 * 60 * 1000; // 1 hour protected static final String[] REASON_STRINGS = { @@ -47,14 +58,14 @@ public class SelfRecovery { "Sta Interface Down" // REASON_STA_IFACE_DOWN }; - private final WifiController mWifiController; + private final ActiveModeWarden mActiveModeWarden; private final Clock mClock; // Time since boot (in millis) that restart occurred private final LinkedList<Long> mPastRestartTimes; - public SelfRecovery(WifiController wifiController, Clock clock) { - mWifiController = wifiController; + public SelfRecovery(ActiveModeWarden activeModeWarden, Clock clock) { + mActiveModeWarden = activeModeWarden; mClock = clock; - mPastRestartTimes = new LinkedList<Long>(); + mPastRestartTimes = new LinkedList<>(); } /** @@ -63,13 +74,13 @@ public class SelfRecovery { * This method does the following: * 1. Checks reason code used to trigger recovery * 2. Checks for sta iface down triggers and disables wifi by sending {@link - * WifiController#CMD_RECOVERY_DISABLE_WIFI} to {@link WifiController} to disable wifi. + * ActiveModeWarden#recoveryDisableWifi()} to {@link ActiveModeWarden} to disable wifi. * 3. Throttles restart calls for underlying native failures - * 4. Sends {@link WifiController#CMD_RECOVERY_RESTART_WIFI} to {@link WifiController} to + * 4. Sends {@link ActiveModeWarden#recoveryRestartWifi(int)} to {@link ActiveModeWarden} to * initiate the stack restart. * @param reason One of the above |REASON_*| codes. */ - public void trigger(int reason) { + public void trigger(@RecoveryReason int reason) { if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_WIFINATIVE_FAILURE || reason == REASON_STA_IFACE_DOWN)) { Log.e(TAG, "Invalid trigger reason. Ignoring..."); @@ -77,7 +88,7 @@ public class SelfRecovery { } if (reason == REASON_STA_IFACE_DOWN) { Log.e(TAG, "STA interface down, disable wifi"); - mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); + mActiveModeWarden.recoveryDisableWifi(); return; } @@ -88,12 +99,12 @@ public class SelfRecovery { if (mPastRestartTimes.size() >= MAX_RESTARTS_IN_TIME_WINDOW) { Log.e(TAG, "Already restarted wifi (" + MAX_RESTARTS_IN_TIME_WINDOW + ") times in" + " last (" + MAX_RESTARTS_TIME_WINDOW_MILLIS + "ms ). Disabling wifi"); - mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); + mActiveModeWarden.recoveryDisableWifi(); return; } mPastRestartTimes.add(mClock.getElapsedSinceBootMillis()); } - mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason); + mActiveModeWarden.recoveryRestartWifi(reason); } /** diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java index 79bc46fa56..1f6a5fcc60 100644 --- a/service/java/com/android/server/wifi/SoftApManager.java +++ b/service/java/com/android/server/wifi/SoftApManager.java @@ -24,7 +24,9 @@ import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; +import android.net.MacAddress; import android.net.wifi.ScanResult; +import android.net.wifi.WifiClient; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Handler; @@ -45,9 +47,14 @@ import com.android.internal.util.WakeupMessage; import com.android.server.wifi.WifiNative.InterfaceCallback; import com.android.server.wifi.WifiNative.SoftApListener; import com.android.server.wifi.util.ApConfigUtil; +import com.android.server.wifi.wificond.NativeWifiClient; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import java.util.Locale; /** @@ -72,7 +79,8 @@ public class SoftApManager implements ActiveModeManager { private final SoftApStateMachine mStateMachine; - private final WifiManager.SoftApCallback mCallback; + private final Listener mModeListener; + private final WifiManager.SoftApCallback mSoftApCallback; private String mApInterfaceName; private boolean mIfaceIsUp; @@ -82,18 +90,20 @@ public class SoftApManager implements ActiveModeManager { private final WifiMetrics mWifiMetrics; - private final int mMode; - private WifiConfiguration mApConfig; + @NonNull + private SoftApModeConfiguration mApConfig; private int mReportedFrequency = -1; private int mReportedBandwidth = -1; - private int mNumAssociatedStations = 0; + private List<WifiClient> mConnectedClients = new ArrayList<>(); private boolean mTimeoutEnabled = false; private final SarManager mSarManager; - private long mStartTimestamp = -1; + private String mStartTimestamp; + + private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); private BaseWifiDiagnostics mWifiDiagnostics; @@ -108,9 +118,9 @@ public class SoftApManager implements ActiveModeManager { } @Override - public void onNumAssociatedStationsChanged(int numStations) { - mStateMachine.sendMessage( - SoftApStateMachine.CMD_NUM_ASSOCIATED_STATIONS_CHANGED, numStations); + public void onConnectedClientsChanged(List<NativeWifiClient> clients) { + mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED, + clients); } @Override @@ -125,6 +135,7 @@ public class SoftApManager implements ActiveModeManager { @NonNull FrameworkFacade framework, @NonNull WifiNative wifiNative, String countryCode, + @NonNull Listener listener, @NonNull WifiManager.SoftApCallback callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, @@ -135,14 +146,13 @@ public class SoftApManager implements ActiveModeManager { mFrameworkFacade = framework; mWifiNative = wifiNative; mCountryCode = countryCode; - mCallback = callback; + mModeListener = listener; + mSoftApCallback = callback; mWifiApConfigStore = wifiApConfigStore; - mMode = apConfig.getTargetMode(); - WifiConfiguration config = apConfig.getWifiConfiguration(); - if (config == null) { - mApConfig = mWifiApConfigStore.getApConfiguration(); - } else { - mApConfig = config; + mApConfig = apConfig; + if (mApConfig.getWifiConfiguration() == null) { + WifiConfiguration wifiConfig = mWifiApConfigStore.getApConfiguration(); + mApConfig = new SoftApModeConfiguration(mApConfig.getTargetMode(), wifiConfig); } mWifiMetrics = wifiMetrics; mSarManager = sarManager; @@ -151,10 +161,10 @@ public class SoftApManager implements ActiveModeManager { } /** - * Start soft AP with the supplied config. + * Start soft AP, as configured in the constructor. */ public void start() { - mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig); + mStateMachine.sendMessage(SoftApStateMachine.CMD_START); } /** @@ -179,7 +189,7 @@ public class SoftApManager implements ActiveModeManager { } public int getIpMode() { - return mMode; + return mApConfig.getTargetMode(); } /** @@ -191,20 +201,18 @@ public class SoftApManager implements ActiveModeManager { pw.println("current StateMachine mode: " + getCurrentStateName()); pw.println("mApInterfaceName: " + mApInterfaceName); pw.println("mIfaceIsUp: " + mIfaceIsUp); - pw.println("mMode: " + mMode); - pw.println("mCountryCode: " + mCountryCode); - if (mApConfig != null) { - pw.println("mApConfig.SSID: " + mApConfig.SSID); - pw.println("mApConfig.apBand: " + mApConfig.apBand); - pw.println("mApConfig.hiddenSSID: " + mApConfig.hiddenSSID); - } else { - pw.println("mApConfig: null"); - } - pw.println("mNumAssociatedStations: " + mNumAssociatedStations); + pw.println("mSoftApCountryCode: " + mCountryCode); + pw.println("mApConfig.targetMode: " + mApConfig.getTargetMode()); + WifiConfiguration wifiConfig = mApConfig.getWifiConfiguration(); + pw.println("mApConfig.wifiConfiguration.SSID: " + wifiConfig.SSID); + pw.println("mApConfig.wifiConfiguration.apBand: " + wifiConfig.apBand); + pw.println("mApConfig.wifiConfiguration.hiddenSSID: " + wifiConfig.hiddenSSID); + pw.println("mConnectedClients.size(): " + mConnectedClients.size()); pw.println("mTimeoutEnabled: " + mTimeoutEnabled); pw.println("mReportedFrequency: " + mReportedFrequency); pw.println("mReportedBandwidth: " + mReportedBandwidth); pw.println("mStartTimestamp: " + mStartTimestamp); + mStateMachine.dump(fd, pw, args); } private String getCurrentStateName() { @@ -219,12 +227,13 @@ public class SoftApManager implements ActiveModeManager { /** * Update AP state. - * @param newState new AP state + * + * @param newState new AP state * @param currentState current AP state - * @param reason Failure reason if the new AP state is in failure state + * @param reason Failure reason if the new AP state is in failure state */ private void updateApState(int newState, int currentState, int reason) { - mCallback.onStateChanged(newState, reason); + mSoftApCallback.onStateChanged(newState, reason); //send the AP state change broadcast final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); @@ -237,45 +246,91 @@ public class SoftApManager implements ActiveModeManager { } intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName); - intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mMode); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mApConfig.getTargetMode()); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - /** - * Start a soft AP instance with the given configuration. - * @param config AP configuration - * @return integer result code - */ - private int startSoftAp(WifiConfiguration config) { - if (config == null || config.SSID == null) { - Log.e(TAG, "Unable to start soft AP without valid configuration"); + private int setMacAddress() { + boolean randomize = mContext.getResources().getBoolean( + R.bool.config_wifi_ap_mac_randomization_supported); + if (!randomize) { + MacAddress mac = mWifiNative.getFactoryMacAddress(mApInterfaceName); + if (mac == null) { + Log.e(TAG, "failed to get factory MAC address"); + return ERROR_GENERIC; + } + + // We're (re-)configuring the factory MAC address. Some drivers may not support setting + // the MAC at all, so fail soft in this case. + if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) { + Log.w(TAG, "failed to reset to factory MAC address; continuing with current MAC"); + } + return SUCCESS; + } + + // We're configuring a random MAC address. In this case, driver support is mandatory. + MacAddress mac = MacAddress.createRandomUnicastAddress(); + if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) { + Log.e(TAG, "failed to set random MAC address"); return ERROR_GENERIC; } - // Setup country code + return SUCCESS; + } + + private int setCountryCode() { + int band = mApConfig.getWifiConfiguration().apBand; if (TextUtils.isEmpty(mCountryCode)) { - if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) { + if (band == WifiConfiguration.AP_BAND_5GHZ) { // Country code is mandatory for 5GHz band. - Log.e(TAG, "Invalid country code, required for setting up " - + "soft ap in 5GHz"); + Log.e(TAG, "Invalid country code, required for setting up soft ap in 5GHz"); return ERROR_GENERIC; } // Absence of country code is not fatal for 2Ghz & Any band options. - } else if (!mWifiNative.setCountryCodeHal( + return SUCCESS; + } + + if (!mWifiNative.setCountryCodeHal( mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) { - if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) { + if (band == WifiConfiguration.AP_BAND_5GHZ) { // Return an error if failed to set country code when AP is configured for // 5GHz band. - Log.e(TAG, "Failed to set country code, required for setting up " - + "soft ap in 5GHz"); + Log.e(TAG, "Failed to set country code, required for setting up soft ap in 5GHz"); return ERROR_GENERIC; } // Failure to set country code is not fatal for 2Ghz & Any band options. } + return SUCCESS; + } + + /** + * Start a soft AP instance as configured. + * + * @return integer result code + */ + private int startSoftAp() { + WifiConfiguration config = mApConfig.getWifiConfiguration(); + if (config == null || config.SSID == null) { + Log.e(TAG, "Unable to start soft AP without valid configuration"); + return ERROR_GENERIC; + } + + Log.d(TAG, "band " + config.apBand + " iface " + + mApInterfaceName + " country " + mCountryCode); + + int result = setMacAddress(); + if (result != SUCCESS) { + return result; + } + + result = setCountryCode(); + if (result != SUCCESS) { + return result; + } // Make a copy of configuration for updating AP band and channel. WifiConfiguration localConfig = new WifiConfiguration(config); - int result = ApConfigUtil.updateApChannelConfig( + result = ApConfigUtil.updateApChannelConfig( mWifiNative, mCountryCode, mWifiApConfigStore.getAllowed2GChannel(), localConfig); @@ -287,13 +342,14 @@ public class SoftApManager implements ActiveModeManager { if (localConfig.hiddenSSID) { Log.d(TAG, "SoftAP is a hidden network"); } + if (!mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener)) { Log.e(TAG, "Soft AP start failed"); return ERROR_GENERIC; } mWifiDiagnostics.startLogging(mApInterfaceName); - mStartTimestamp = SystemClock.elapsedRealtime(); - Log.d(TAG, "Soft AP is started"); + mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); + Log.d(TAG, "Soft AP is started "); return SUCCESS; } @@ -312,7 +368,7 @@ public class SoftApManager implements ActiveModeManager { public static final int CMD_START = 0; public static final int CMD_FAILURE = 2; public static final int CMD_INTERFACE_STATUS_CHANGED = 3; - public static final int CMD_NUM_ASSOCIATED_STATIONS_CHANGED = 4; + public static final int CMD_ASSOCIATED_STATIONS_CHANGED = 4; public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5; public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6; public static final int CMD_INTERFACE_DESTROYED = 7; @@ -376,21 +432,23 @@ public class SoftApManager implements ActiveModeManager { WifiManager.SAP_START_FAILURE_GENERAL); mWifiMetrics.incrementSoftApStartResult( false, WifiManager.SAP_START_FAILURE_GENERAL); + mModeListener.onStartFailure(); break; } updateApState(WifiManager.WIFI_AP_STATE_ENABLING, WifiManager.WIFI_AP_STATE_DISABLED, 0); - int result = startSoftAp((WifiConfiguration) message.obj); + int result = startSoftAp(); if (result != SUCCESS) { int failureReason = WifiManager.SAP_START_FAILURE_GENERAL; if (result == ERROR_NO_CHANNEL) { failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; } updateApState(WifiManager.WIFI_AP_STATE_FAILED, - WifiManager.WIFI_AP_STATE_ENABLING, - failureReason); + WifiManager.WIFI_AP_STATE_ENABLING, + failureReason); stopSoftAp(); mWifiMetrics.incrementSoftApStartResult(false, failureReason); + mModeListener.onStartFailure(); break; } transitionTo(mStartedState); @@ -410,8 +468,8 @@ public class SoftApManager implements ActiveModeManager { private SoftApTimeoutEnabledSettingObserver mSettingObserver; /** - * Observer for timeout settings changes. - */ + * Observer for timeout settings changes. + */ private class SoftApTimeoutEnabledSettingObserver extends ContentObserver { SoftApTimeoutEnabledSettingObserver(Handler handler) { super(handler); @@ -467,49 +525,76 @@ public class SoftApManager implements ActiveModeManager { } /** - * Set number of stations associated with this soft AP - * @param numStations Number of connected stations + * Set stations associated with this soft AP + * @param clients The connected stations */ - private void setNumAssociatedStations(int numStations) { - if (mNumAssociatedStations == numStations) { + private void setConnectedClients(List<NativeWifiClient> clients) { + if (clients == null) { return; } - mNumAssociatedStations = numStations; - Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations); - if (mCallback != null) { - mCallback.onNumClientsChanged(mNumAssociatedStations); + List<WifiClient> convertedClients = createWifiClients(clients); + if (mConnectedClients.equals(convertedClients)) { + return; + } + + mConnectedClients = new ArrayList<>(convertedClients); + Log.d(TAG, "The connected wifi stations have changed with count: " + + clients.size()); + + if (mSoftApCallback != null) { + mSoftApCallback.onConnectedClientsChanged(mConnectedClients); } else { - Log.e(TAG, "SoftApCallback is null. Dropping NumClientsChanged event."); + Log.e(TAG, + "SoftApCallback is null. Dropping ConnectedClientsChanged event." + ); } - mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations, - mMode); - if (mNumAssociatedStations == 0) { + mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent( + mConnectedClients.size(), mApConfig.getTargetMode()); + + if (mConnectedClients.size() == 0) { scheduleTimeoutMessage(); } else { cancelTimeoutMessage(); } } + private List<WifiClient> createWifiClients(List<NativeWifiClient> nativeClients) { + List<WifiClient> clients = new ArrayList<>(); + if (nativeClients == null || nativeClients.size() == 0) { + return clients; + } + + for (int i = 0; i < nativeClients.size(); i++) { + MacAddress macAddress = MacAddress.fromBytes(nativeClients.get(i).macAddress); + WifiClient client = new WifiClient(macAddress); + clients.add(client); + } + + return clients; + } + 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, WifiManager.WIFI_AP_STATE_ENABLING, 0); + mModeListener.onStarted(); mWifiMetrics.incrementSoftApStartResult(true, 0); - if (mCallback != null) { - mCallback.onNumClientsChanged(mNumAssociatedStations); + if (mSoftApCallback != null) { + mSoftApCallback.onConnectedClientsChanged(mConnectedClients); } } else { // the interface was up, but goes down sendMessage(CMD_INTERFACE_DOWN); } - mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode); + mWifiMetrics.addSoftApUpChangedEvent(isUp, mApConfig.getTargetMode()); } @Override @@ -531,8 +616,8 @@ public class SoftApManager implements ActiveModeManager { mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); - Log.d(TAG, "Resetting num stations on start"); - mNumAssociatedStations = 0; + Log.d(TAG, "Resetting connected clients on start"); + mConnectedClients = new ArrayList<>(); scheduleTimeoutMessage(); } @@ -546,11 +631,11 @@ public class SoftApManager implements ActiveModeManager { mSettingObserver.unregister(); } Log.d(TAG, "Resetting num stations on stop"); - setNumAssociatedStations(0); + setConnectedClients(new ArrayList<>()); cancelTimeoutMessage(); // Need this here since we are exiting |Started| state and won't handle any // future CMD_INTERFACE_STATUS_CHANGED events after this point - mWifiMetrics.addSoftApUpChangedEvent(false, mMode); + mWifiMetrics.addSoftApUpChangedEvent(false, mApConfig.getTargetMode()); updateApState(WifiManager.WIFI_AP_STATE_DISABLED, WifiManager.WIFI_AP_STATE_DISABLING, 0); @@ -559,17 +644,16 @@ public class SoftApManager implements ActiveModeManager { mIfaceIsUp = false; mIfaceIsDestroyed = false; mStateMachine.quitNow(); + mModeListener.onStopped(); } private void updateUserBandPreferenceViolationMetricsIfNeeded() { - boolean bandPreferenceViolated = false; - if (mApConfig.apBand == WifiConfiguration.AP_BAND_2GHZ - && ScanResult.is5GHz(mReportedFrequency)) { - bandPreferenceViolated = true; - } else if (mApConfig.apBand == WifiConfiguration.AP_BAND_5GHZ - && ScanResult.is24GHz(mReportedFrequency)) { - bandPreferenceViolated = true; - } + int band = mApConfig.getWifiConfiguration().apBand; + boolean bandPreferenceViolated = + (band == WifiConfiguration.AP_BAND_2GHZ + && ScanResult.is5GHz(mReportedFrequency)) + || (band == WifiConfiguration.AP_BAND_5GHZ + && ScanResult.is24GHz(mReportedFrequency)); if (bandPreferenceViolated) { Log.e(TAG, "Channel does not satisfy user band preference: " + mReportedFrequency); @@ -580,13 +664,16 @@ public class SoftApManager implements ActiveModeManager { @Override public boolean processMessage(Message message) { switch (message.what) { - case CMD_NUM_ASSOCIATED_STATIONS_CHANGED: - if (message.arg1 < 0) { - Log.e(TAG, "Invalid number of associated stations: " + message.arg1); + case CMD_ASSOCIATED_STATIONS_CHANGED: + if (!(message.obj instanceof List)) { + Log.e(TAG, "Invalid type returned for" + + " CMD_ASSOCIATED_STATIONS_CHANGED"); break; } - Log.d(TAG, "Setting num stations on CMD_NUM_ASSOCIATED_STATIONS_CHANGED"); - setNumAssociatedStations(message.arg1); + + Log.d(TAG, "Setting connected stations on" + + " CMD_ASSOCIATED_STATIONS_CHANGED"); + setConnectedClients((List<NativeWifiClient>) message.obj); break; case CMD_SOFT_AP_CHANNEL_SWITCHED: mReportedFrequency = message.arg1; @@ -594,7 +681,7 @@ public class SoftApManager implements ActiveModeManager { Log.d(TAG, "Channel switched. Frequency: " + mReportedFrequency + " Bandwidth: " + mReportedBandwidth); mWifiMetrics.addSoftApChannelSwitchedEvent(mReportedFrequency, - mReportedBandwidth, mMode); + mReportedBandwidth, mApConfig.getTargetMode()); updateUserBandPreferenceViolationMetricsIfNeeded(); break; case CMD_TIMEOUT_TOGGLE_CHANGED: @@ -606,7 +693,7 @@ public class SoftApManager implements ActiveModeManager { if (!mTimeoutEnabled) { cancelTimeoutMessage(); } - if (mTimeoutEnabled && mNumAssociatedStations == 0) { + if (mTimeoutEnabled && mConnectedClients.size() == 0) { scheduleTimeoutMessage(); } break; @@ -623,7 +710,7 @@ public class SoftApManager implements ActiveModeManager { + " Dropping."); break; } - if (mNumAssociatedStations != 0) { + if (mConnectedClients.size() != 0) { Log.wtf(TAG, "Timeout message received but has clients. Dropping."); break; } diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java new file mode 100644 index 0000000000..124d252cb2 --- /dev/null +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiSsid; +import android.util.Log; + +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.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +abstract class SupplicantStaIfaceCallbackImpl extends ISupplicantStaIfaceCallback.Stub { + private static final String TAG = SupplicantStaIfaceCallbackImpl.class.getSimpleName(); + private final SupplicantStaIfaceHal mStaIfaceHal; + private final String mIfaceName; + private final Object mLock; + private final WifiMonitor mWifiMonitor; + private boolean mStateIsFourway = false; // Used to help check for PSK password mismatch + + SupplicantStaIfaceCallbackImpl(@NonNull SupplicantStaIfaceHal staIfaceHal, + @NonNull String ifaceName, + @NonNull Object lock, + @NonNull WifiMonitor wifiMonitor) { + mStaIfaceHal = staIfaceHal; + mIfaceName = ifaceName; + mLock = lock; + mWifiMonitor = wifiMonitor; + } + + /** + * 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); + } + } + + + /** + * 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) { + synchronized (mLock) { + try { + return Constants.getANQPElementID(infoID) != null + ? ANQPParser.parseElement( + infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload))) + : ANQPParser.parseHS20Element( + infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload))); + } catch (IOException | BufferUnderflowException e) { + Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e); + return null; + } + } + } + + /** + * 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) { + synchronized (mLock) { + if (payload == null || payload.isEmpty()) return; + ANQPElement element = parseAnqpElement(infoID, payload); + if (element != null) { + elementsMap.put(infoID, element); + } + } + } + + @Override + public void onNetworkAdded(int id) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onNetworkAdded"); + } + } + + @Override + public void onNetworkRemoved(int id) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onNetworkRemoved"); + // Reset 4way handshake state since network has been removed. + mStateIsFourway = false; + } + } + + @Override + public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, + ArrayList<Byte> ssid) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onStateChanged"); + 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, mStaIfaceHal.getCurrentNetworkId(mIfaceName), bssidStr); + } + mWifiMonitor.broadcastSupplicantStateChangeEvent( + mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), wifiSsid, + bssidStr, newSupplicantState); + } + } + + @Override + public void onAnqpQueryDone(byte[/* 6 */] bssid, + ISupplicantStaIfaceCallback.AnqpData data, + ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onAnqpQueryDone"); + 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) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onHs20IconQueryDone"); + 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) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onHs20SubscriptionRemediation"); + mWifiMonitor.broadcastWnmEvent( + mIfaceName, + new WnmData(NativeUtil.macAddressToLong(bssid), url, osuMethod)); + } + } + + @Override + public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode, + int reAuthDelayInSec, String url) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onHs20DeauthImminentNotice"); + mWifiMonitor.broadcastWnmEvent( + mIfaceName, + new WnmData(NativeUtil.macAddressToLong(bssid), url, + reasonCode == WnmData.ESS, reAuthDelayInSec)); + } + } + + @Override + public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onDisconnected"); + if (mStaIfaceHal.isVerboseLoggingEnabled()) { + Log.e(TAG, "onDisconnected 4way=" + mStateIsFourway + + " locallyGenerated=" + locallyGenerated + + " reasonCode=" + reasonCode); + } + if (mStateIsFourway + && (!locallyGenerated || reasonCode != ReasonCode.IE_IN_4WAY_DIFFERS)) { + mWifiMonitor.broadcastAuthenticationFailureEvent( + mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1); + } + mWifiMonitor.broadcastNetworkDisconnectionEvent( + mIfaceName, locallyGenerated ? 1 : 0, reasonCode, + NativeUtil.macAddressFromByteArray(bssid)); + } + } + + @Override + public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, boolean timedOut) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onAssociationRejected"); + + if (statusCode == StatusCode.UNSPECIFIED_FAILURE) { + WifiConfiguration curConfiguration = + mStaIfaceHal.getCurrentNetworkLocalConfig(mIfaceName); + + if (curConfiguration != null + && curConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.SAE)) { + // Special handling for WPA3-Personal networks. If the password is + // incorrect, the AP will send association rejection, with status code 1 + // (unspecified failure). In SAE networks, the password authentication + // is not related to the 4-way handshake. In this case, we will send an + // authentication failure event up. + mStaIfaceHal.logCallback("SAE incorrect password"); + mWifiMonitor.broadcastAuthenticationFailureEvent( + mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1); + } + } + mWifiMonitor + .broadcastAssociationRejectionEvent( + mIfaceName, statusCode, timedOut, + NativeUtil.macAddressFromByteArray(bssid)); + } + } + + @Override + public void onAuthenticationTimeout(byte[/* 6 */] bssid) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onAuthenticationTimeout"); + mWifiMonitor.broadcastAuthenticationFailureEvent( + mIfaceName, WifiManager.ERROR_AUTH_FAILURE_TIMEOUT, -1); + } + } + + @Override + public void onBssidChanged(byte reason, byte[/* 6 */] bssid) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onBssidChanged"); + 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() { + synchronized (mLock) { + mStaIfaceHal.logCallback("onEapFailure"); + mWifiMonitor.broadcastAuthenticationFailureEvent( + mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, -1); + } + } + + @Override + public void onWpsEventSuccess() { + mStaIfaceHal.logCallback("onWpsEventSuccess"); + synchronized (mLock) { + mWifiMonitor.broadcastWpsSuccessEvent(mIfaceName); + } + } + + @Override + public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onWpsEventFail"); + if (configError == WpsConfigError.MSG_TIMEOUT + && errorInd == WpsErrorIndication.NO_ERROR) { + mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName); + } else { + mWifiMonitor.broadcastWpsFailEvent(mIfaceName, configError, errorInd); + } + } + } + + @Override + public void onWpsEventPbcOverlap() { + synchronized (mLock) { + mStaIfaceHal.logCallback("onWpsEventPbcOverlap"); + mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName); + } + } + + @Override + public void onExtRadioWorkStart(int id) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onExtRadioWorkStart"); + } + } + + @Override + public void onExtRadioWorkTimeout(int id) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onExtRadioWorkTimeout"); + } + } +} diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_1Impl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_1Impl.java new file mode 100644 index 0000000000..12e025e69f --- /dev/null +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_1Impl.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; +import android.net.wifi.WifiManager; + +import java.util.ArrayList; + +abstract class SupplicantStaIfaceCallbackV1_1Impl extends + android.hardware.wifi.supplicant.V1_1.ISupplicantStaIfaceCallback.Stub { + private static final String TAG = SupplicantStaIfaceCallbackV1_1Impl.class.getSimpleName(); + private final SupplicantStaIfaceHal mStaIfaceHal; + private final String mIfaceName; + private final Object mLock; + private final WifiMonitor mWifiMonitor; + private final SupplicantStaIfaceHal.SupplicantStaIfaceHalCallback mCallbackV10; + + SupplicantStaIfaceCallbackV1_1Impl(@NonNull SupplicantStaIfaceHal staIfaceHal, + @NonNull String ifaceName, + @NonNull Object lock, + @NonNull WifiMonitor wifiMonitor) { + mStaIfaceHal = staIfaceHal; + mIfaceName = ifaceName; + mLock = lock; + mWifiMonitor = wifiMonitor; + // Create an older callback for function delegation, + // and it would cascadingly create older one. + mCallbackV10 = mStaIfaceHal.new SupplicantStaIfaceHalCallback(mIfaceName); + } + + @Override + public void onNetworkAdded(int id) { + mCallbackV10.onNetworkAdded(id); + } + + @Override + public void onNetworkRemoved(int id) { + mCallbackV10.onNetworkRemoved(id); + } + + @Override + public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, + ArrayList<Byte> ssid) { + mCallbackV10.onStateChanged(newState, bssid, id, ssid); + } + + @Override + public void onAnqpQueryDone(byte[/* 6 */] bssid, + ISupplicantStaIfaceCallback.AnqpData data, + ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) { + mCallbackV10.onAnqpQueryDone(bssid, data, hs20Data); + } + + @Override + public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName, + ArrayList<Byte> data) { + mCallbackV10.onHs20IconQueryDone(bssid, fileName, data); + } + + @Override + public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, + byte osuMethod, String url) { + mCallbackV10.onHs20SubscriptionRemediation(bssid, osuMethod, url); + } + + @Override + public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode, + int reAuthDelayInSec, String url) { + mCallbackV10.onHs20DeauthImminentNotice(bssid, reasonCode, reAuthDelayInSec, url); + } + + @Override + public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, + int reasonCode) { + mCallbackV10.onDisconnected(bssid, locallyGenerated, reasonCode); + } + + @Override + public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, + boolean timedOut) { + mCallbackV10.onAssociationRejected(bssid, statusCode, timedOut); + } + + @Override + public void onAuthenticationTimeout(byte[/* 6 */] bssid) { + mCallbackV10.onAuthenticationTimeout(bssid); + } + + @Override + public void onBssidChanged(byte reason, byte[/* 6 */] bssid) { + mCallbackV10.onBssidChanged(reason, bssid); + } + + @Override + public void onEapFailure() { + mCallbackV10.onEapFailure(); + } + + @Override + public void onEapFailure_1_1(int code) { + synchronized (mLock) { + mStaIfaceHal.logCallback("onEapFailure_1_1"); + mWifiMonitor.broadcastAuthenticationFailureEvent( + mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, code); + } + } + + @Override + public void onWpsEventSuccess() { + mCallbackV10.onWpsEventSuccess(); + } + + @Override + public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) { + mCallbackV10.onWpsEventFail(bssid, configError, errorInd); + } + + @Override + public void onWpsEventPbcOverlap() { + mCallbackV10.onWpsEventPbcOverlap(); + } + + @Override + public void onExtRadioWorkStart(int id) { + mCallbackV10.onExtRadioWorkStart(id); + } + + @Override + public void onExtRadioWorkTimeout(int id) { + mCallbackV10.onExtRadioWorkTimeout(id); + } +} diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_2Impl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_2Impl.java new file mode 100644 index 0000000000..b80911736c --- /dev/null +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_2Impl.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.content.Context; +import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; +import android.hardware.wifi.supplicant.V1_2.DppAkm; +import android.hardware.wifi.supplicant.V1_2.DppFailureCode; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiSsid; +import android.os.Process; +import android.util.Log; + +import com.android.server.wifi.util.NativeUtil; + +import java.util.ArrayList; + +abstract class SupplicantStaIfaceCallbackV1_2Impl extends + android.hardware.wifi.supplicant.V1_2.ISupplicantStaIfaceCallback.Stub { + private static final String TAG = SupplicantStaIfaceCallbackV1_2Impl.class.getSimpleName(); + private final SupplicantStaIfaceHal mStaIfaceHal; + private final String mIfaceName; + private final Context mContext; + private final SupplicantStaIfaceHal.SupplicantStaIfaceHalCallbackV1_1 mCallbackV11; + + SupplicantStaIfaceCallbackV1_2Impl(@NonNull SupplicantStaIfaceHal staIfaceHal, + @NonNull String ifaceName, + @NonNull Context context) { + mStaIfaceHal = staIfaceHal; + mIfaceName = ifaceName; + mContext = context; + // Create an older callback for function delegation, + // and it would cascadingly create older one. + mCallbackV11 = mStaIfaceHal.new SupplicantStaIfaceHalCallbackV1_1(mIfaceName); + } + + @Override + public void onNetworkAdded(int id) { + mCallbackV11.onNetworkAdded(id); + } + + @Override + public void onNetworkRemoved(int id) { + mCallbackV11.onNetworkRemoved(id); + } + + @Override + public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, + ArrayList<Byte> ssid) { + mCallbackV11.onStateChanged(newState, bssid, id, ssid); + } + + @Override + public void onAnqpQueryDone(byte[/* 6 */] bssid, + ISupplicantStaIfaceCallback.AnqpData data, + ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) { + mCallbackV11.onAnqpQueryDone(bssid, data, hs20Data); + } + + @Override + public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName, + ArrayList<Byte> data) { + mCallbackV11.onHs20IconQueryDone(bssid, fileName, data); + } + + @Override + public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, + byte osuMethod, String url) { + mCallbackV11.onHs20SubscriptionRemediation(bssid, osuMethod, url); + } + + @Override + public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode, + int reAuthDelayInSec, String url) { + mCallbackV11.onHs20DeauthImminentNotice(bssid, reasonCode, reAuthDelayInSec, url); + } + + @Override + public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, + int reasonCode) { + mCallbackV11.onDisconnected(bssid, locallyGenerated, reasonCode); + } + + @Override + public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, + boolean timedOut) { + mCallbackV11.onAssociationRejected(bssid, statusCode, timedOut); + } + + @Override + public void onAuthenticationTimeout(byte[/* 6 */] bssid) { + mCallbackV11.onAuthenticationTimeout(bssid); + } + + @Override + public void onBssidChanged(byte reason, byte[/* 6 */] bssid) { + mCallbackV11.onBssidChanged(reason, bssid); + } + + @Override + public void onEapFailure() { + mCallbackV11.onEapFailure(); + } + + @Override + public void onEapFailure_1_1(int code) { + mCallbackV11.onEapFailure_1_1(code); + } + + @Override + public void onWpsEventSuccess() { + mCallbackV11.onWpsEventSuccess(); + } + + @Override + public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) { + mCallbackV11.onWpsEventFail(bssid, configError, errorInd); + } + + @Override + public void onWpsEventPbcOverlap() { + mCallbackV11.onWpsEventPbcOverlap(); + } + + @Override + public void onExtRadioWorkStart(int id) { + mCallbackV11.onExtRadioWorkStart(id); + } + + @Override + public void onExtRadioWorkTimeout(int id) { + mCallbackV11.onExtRadioWorkTimeout(id); + } + + @Override + public void onDppSuccessConfigReceived(ArrayList<Byte> ssid, String password, + byte[] psk, int securityAkm) { + if (mStaIfaceHal.getDppCallback() == null) { + Log.e(TAG, "onDppSuccessConfigReceived callback is null"); + return; + } + + WifiConfiguration newWifiConfiguration = new WifiConfiguration(); + + // Set up SSID + WifiSsid wifiSsid = + WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid)); + + newWifiConfiguration.SSID = "\"" + wifiSsid.toString() + "\""; + + // Set up password or PSK + if (password != null) { + newWifiConfiguration.preSharedKey = "\"" + password + "\""; + } else if (psk != null) { + newWifiConfiguration.preSharedKey = psk.toString(); + } + + // Set up key management: SAE or PSK + if (securityAkm == DppAkm.SAE || securityAkm == DppAkm.PSK_SAE) { + newWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); + newWifiConfiguration.requirePMF = true; + } else if (securityAkm == DppAkm.PSK) { + newWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + } else { + // No other AKMs are currently supported + onDppFailure(DppFailureCode.NOT_SUPPORTED); + return; + } + + // Set up default values + newWifiConfiguration.creatorName = mContext.getPackageManager() + .getNameForUid(Process.WIFI_UID); + newWifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); + newWifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + newWifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + newWifiConfiguration.status = WifiConfiguration.Status.ENABLED; + + mStaIfaceHal.getDppCallback().onSuccessConfigReceived(newWifiConfiguration); + } + + @Override + public void onDppSuccessConfigSent() { + if (mStaIfaceHal.getDppCallback() != null) { + mStaIfaceHal.getDppCallback().onSuccessConfigSent(); + } else { + Log.e(TAG, "onSuccessConfigSent callback is null"); + } + } + + @Override + public void onDppProgress(int code) { + if (mStaIfaceHal.getDppCallback() != null) { + mStaIfaceHal.getDppCallback().onProgress(code); + } else { + Log.e(TAG, "onDppProgress callback is null"); + } + } + + @Override + public void onDppFailure(int code) { + if (mStaIfaceHal.getDppCallback() != null) { + mStaIfaceHal.getDppCallback().onFailure(code); + } else { + Log.e(TAG, "onDppFailure callback is null"); + } + } +} diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java new file mode 100644 index 0000000000..97b984a7a5 --- /dev/null +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; + +import java.util.ArrayList; + +abstract class SupplicantStaIfaceCallbackV1_3Impl extends + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback.Stub { + private static final String TAG = SupplicantStaIfaceCallbackV1_3Impl.class.getSimpleName(); + private final SupplicantStaIfaceHal mStaIfaceHal; + private final String mIfaceName; + private final SupplicantStaIfaceHal.SupplicantStaIfaceHalCallbackV1_2 mCallbackV12; + + SupplicantStaIfaceCallbackV1_3Impl(@NonNull SupplicantStaIfaceHal staIfaceHal, + @NonNull String ifaceName) { + mStaIfaceHal = staIfaceHal; + mIfaceName = ifaceName; + // Create an older callback for function delegation, + // and it would cascadingly create older one. + mCallbackV12 = mStaIfaceHal.new SupplicantStaIfaceHalCallbackV1_2(mIfaceName); + } + + @Override + public void onNetworkAdded(int id) { + mCallbackV12.onNetworkAdded(id); + } + + @Override + public void onNetworkRemoved(int id) { + mCallbackV12.onNetworkRemoved(id); + } + + @Override + public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, + ArrayList<Byte> ssid) { + mCallbackV12.onStateChanged(newState, bssid, id, ssid); + } + + @Override + public void onAnqpQueryDone(byte[/* 6 */] bssid, + ISupplicantStaIfaceCallback.AnqpData data, + ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) { + mCallbackV12.onAnqpQueryDone(bssid, data, hs20Data); + } + + @Override + public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName, + ArrayList<Byte> data) { + mCallbackV12.onHs20IconQueryDone(bssid, fileName, data); + } + + @Override + public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, + byte osuMethod, String url) { + mCallbackV12.onHs20SubscriptionRemediation(bssid, osuMethod, url); + } + + @Override + public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode, + int reAuthDelayInSec, String url) { + mCallbackV12.onHs20DeauthImminentNotice(bssid, reasonCode, reAuthDelayInSec, url); + } + + @Override + public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, + int reasonCode) { + mCallbackV12.onDisconnected(bssid, locallyGenerated, reasonCode); + } + + @Override + public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, + boolean timedOut) { + mCallbackV12.onAssociationRejected(bssid, statusCode, timedOut); + } + + @Override + public void onAuthenticationTimeout(byte[/* 6 */] bssid) { + mCallbackV12.onAuthenticationTimeout(bssid); + } + + @Override + public void onBssidChanged(byte reason, byte[/* 6 */] bssid) { + mCallbackV12.onBssidChanged(reason, bssid); + } + + @Override + public void onEapFailure() { + mCallbackV12.onEapFailure(); + } + + @Override + public void onEapFailure_1_1(int code) { + mCallbackV12.onEapFailure_1_1(code); + } + + @Override + public void onWpsEventSuccess() { + mCallbackV12.onWpsEventSuccess(); + } + + @Override + public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) { + mCallbackV12.onWpsEventFail(bssid, configError, errorInd); + } + + @Override + public void onWpsEventPbcOverlap() { + mCallbackV12.onWpsEventPbcOverlap(); + } + + @Override + public void onExtRadioWorkStart(int id) { + mCallbackV12.onExtRadioWorkStart(id); + } + + @Override + public void onExtRadioWorkTimeout(int id) { + mCallbackV12.onExtRadioWorkTimeout(id); + } + + @Override + public void onDppSuccessConfigReceived(ArrayList<Byte> ssid, String password, + byte[] psk, int securityAkm) { + mCallbackV12.onDppSuccessConfigReceived( + ssid, password, psk, securityAkm); + } + + @Override + public void onDppSuccessConfigSent() { + mCallbackV12.onDppSuccessConfigSent(); + } + + @Override + public void onDppProgress(int code) { + mCallbackV12.onDppProgress(code); + } + + @Override + public void onDppFailure(int code) { + mCallbackV12.onDppFailure(code); + } + + @Override + public void onPmkCacheAdded(long expirationTimeInSec, ArrayList<Byte> serializedEntry) { + int curNetworkId = mStaIfaceHal.getCurrentNetworkId(mIfaceName); + mStaIfaceHal.addPmkCacheEntry(curNetworkId, expirationTimeInSec, serializedEntry); + mStaIfaceHal.logCallback( + "onPmkCacheAdded: update pmk cache for config id " + curNetworkId); + } +} diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java index 86518c7613..8db806e597 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java @@ -20,17 +20,6 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; -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; @@ -43,46 +32,32 @@ 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.hardware.wifi.supplicant.V1_2.DppAkm; -import android.hardware.wifi.supplicant.V1_2.DppFailureCode; +import android.hardware.wifi.supplicant.V1_3.ConnectionCapabilities; +import android.hardware.wifi.supplicant.V1_3.WifiTechnology; 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.net.wifi.WifiInfo; import android.os.Handler; -import android.os.HidlSupport.Mutable; import android.os.HwRemoteBinder; -import android.os.Looper; -import android.os.Process; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; import android.util.MutableBoolean; import android.util.MutableInt; import android.util.Pair; -import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.WifiNative.DppEventCallback; import com.android.server.wifi.WifiNative.SupplicantDeathEventHandler; -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.GeneralUtil.Mutable; 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.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; @@ -113,6 +88,8 @@ public class SupplicantStaIfaceHal { public static final String INIT_SERVICE_NAME = "wpa_supplicant"; @VisibleForTesting public static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L; + @VisibleForTesting + static final String PMK_CACHE_EXPIRATION_ALARM_TAG = "PMK_CACHE_EXPIRATION_TIMER"; /** * Regex pattern for extracting the wps device type bytes. * Matches a strings like the following: "<categ>-<OUI>-<subcateg>"; @@ -131,6 +108,8 @@ public class SupplicantStaIfaceHal { new HashMap<>(); private HashMap<String, SupplicantStaNetworkHal> mCurrentNetworkRemoteHandles = new HashMap<>(); private HashMap<String, WifiConfiguration> mCurrentNetworkLocalConfigs = new HashMap<>(); + @VisibleForTesting + HashMap<Integer, PmkCacheStoreData> mPmkCacheEntries = new HashMap<>(); private SupplicantDeathEventHandler mDeathEventHandler; private ServiceManagerDeathRecipient mServiceManagerDeathRecipient; private SupplicantDeathRecipient mSupplicantDeathRecipient; @@ -141,6 +120,7 @@ public class SupplicantStaIfaceHal { private final PropertyService mPropertyService; private final Handler mEventHandler; private DppEventCallback mDppCallback = null; + private final Clock mClock; private final IServiceNotification mServiceNotificationCallback = new IServiceNotification.Stub() { @@ -183,12 +163,25 @@ public class SupplicantStaIfaceHal { } } + @VisibleForTesting + static class PmkCacheStoreData { + public long expirationTimeInSec; + public ArrayList<Byte> data; + + PmkCacheStoreData(long timeInSec, ArrayList<Byte> serializedData) { + expirationTimeInSec = timeInSec; + data = serializedData; + } + } + public SupplicantStaIfaceHal(Context context, WifiMonitor monitor, - PropertyService propertyService, Looper looper) { + PropertyService propertyService, Handler handler, + Clock clock) { mContext = context; mWifiMonitor = monitor; mPropertyService = propertyService; - mEventHandler = new Handler(looper); + mEventHandler = handler; + mClock = clock; mServiceManagerDeathRecipient = new ServiceManagerDeathRecipient(); mSupplicantDeathRecipient = new SupplicantDeathRecipient(); @@ -205,6 +198,10 @@ public class SupplicantStaIfaceHal { } } + protected boolean isVerboseLoggingEnabled() { + return mVerboseLoggingEnabled; + } + private boolean linkToServiceManagerDeath() { synchronized (mLock) { if (mIServiceManager == null) return false; @@ -307,7 +304,7 @@ public class SupplicantStaIfaceHal { return true; } - private int getCurrentNetworkId(@NonNull String ifaceName) { + protected int getCurrentNetworkId(@NonNull String ifaceName) { synchronized (mLock) { WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName); if (currentConfig == null) { @@ -317,6 +314,94 @@ public class SupplicantStaIfaceHal { } } + private boolean trySetupStaIfaceV1_3(@NonNull String ifaceName, + @NonNull ISupplicantStaIface iface) throws RemoteException { + if (!isV1_3()) return false; + + SupplicantStaIfaceHalCallbackV1_3 callbackV13 = + new SupplicantStaIfaceHalCallbackV1_3(ifaceName); + if (!registerCallbackV1_3(getStaIfaceMockableV1_3(iface), callbackV13)) { + throw new RemoteException("Init StaIface V1_3 failed."); + } + /* keep this in a store to avoid recycling by garbage collector. */ + mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV13); + return true; + } + + private boolean trySetupStaIfaceV1_2(@NonNull String ifaceName, + @NonNull ISupplicantStaIface iface) throws RemoteException { + if (!isV1_2()) return false; + + /* try newer version fist. */ + if (trySetupStaIfaceV1_3(ifaceName, iface)) { + logd("Newer HAL is found, skip V1_2 remaining init flow."); + return true; + } + + SupplicantStaIfaceHalCallbackV1_2 callbackV12 = + new SupplicantStaIfaceHalCallbackV1_2(ifaceName); + if (!registerCallbackV1_2(getStaIfaceMockableV1_2(iface), callbackV12)) { + throw new RemoteException("Init StaIface V1_2 failed."); + } + /* keep this in a store to avoid recycling by garbage collector. */ + mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV12); + return true; + } + + private boolean trySetupStaIfaceV1_1(@NonNull String ifaceName, + @NonNull ISupplicantStaIface iface) throws RemoteException { + if (!isV1_1()) return false; + + /* try newer version fist. */ + if (trySetupStaIfaceV1_2(ifaceName, iface)) { + logd("Newer HAL is found, skip V1_1 remaining init flow."); + return true; + } + + SupplicantStaIfaceHalCallbackV1_1 callbackV11 = + new SupplicantStaIfaceHalCallbackV1_1(ifaceName); + if (!registerCallbackV1_1(getStaIfaceMockableV1_1(iface), callbackV11)) { + throw new RemoteException("Init StaIface V1_1 failed."); + } + /* keep this in a store to avoid recycling by garbage collector. */ + mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV11); + return true; + } + + /** + * Helper function to set up StaIface with different HAL version. + * + * This helper function would try newer version recursively. + * Once the latest version is found, it would register the callback + * of the latest version and skip unnecessary older HAL init flow. + * + * New version callback will be extended from the older one, as a result, + * older callback is always created regardless of the latest version. + * + * Uprev steps: + * 1. add new helper function trySetupStaIfaceV1_Y. + * 2. call newly added function in trySetupStaIfaceV1_X (X should be Y-1). + */ + private ISupplicantStaIface setupStaIface(@NonNull String ifaceName, + @NonNull ISupplicantIface ifaceHwBinder) throws RemoteException { + /* Prepare base type for later cast. */ + ISupplicantStaIface iface = getStaIfaceMockable(ifaceHwBinder); + + /* try newer version first. */ + if (trySetupStaIfaceV1_1(ifaceName, iface)) { + logd("Newer HAL is found, skip V1_0 remaining init flow."); + return iface; + } + + SupplicantStaIfaceHalCallback callback = new SupplicantStaIfaceHalCallback(ifaceName); + if (!registerCallback(iface, callback)) { + throw new RemoteException("Init StaIface V1_0 failed."); + } + /* keep this in a store to avoid recycling by garbage collector. */ + mISupplicantStaIfaceCallbacks.put(ifaceName, callback); + return iface; + } + /** * Setup a STA interface for the specified iface name. * @@ -337,43 +422,15 @@ public class SupplicantStaIfaceHal { Log.e(TAG, "setupIface got null iface"); return false; } - SupplicantStaIfaceHalCallback callback = new SupplicantStaIfaceHalCallback(ifaceName); - - if (isV1_2()) { - android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface iface = - getStaIfaceMockableV1_2(ifaceHwBinder); - - SupplicantStaIfaceHalCallbackV1_1 callbackV11 = - new SupplicantStaIfaceHalCallbackV1_1(ifaceName, callback); - - SupplicantStaIfaceHalCallbackV1_2 callbackV12 = - new SupplicantStaIfaceHalCallbackV1_2(callbackV11); - if (!registerCallbackV1_2(iface, callbackV12)) { - return false; - } - mISupplicantStaIfaces.put(ifaceName, iface); - mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV11); - } else if (isV1_1()) { - android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface iface = - getStaIfaceMockableV1_1(ifaceHwBinder); - SupplicantStaIfaceHalCallbackV1_1 callbackV1_1 = - new SupplicantStaIfaceHalCallbackV1_1(ifaceName, callback); - - if (!registerCallbackV1_1(iface, callbackV1_1)) { - return false; - } - mISupplicantStaIfaces.put(ifaceName, iface); - mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV1_1); - } else { - ISupplicantStaIface iface = getStaIfaceMockable(ifaceHwBinder); - - if (!registerCallback(iface, callback)) { - return false; - } + try { + ISupplicantStaIface iface = setupStaIface(ifaceName, ifaceHwBinder); mISupplicantStaIfaces.put(ifaceName, iface); - mISupplicantStaIfaceCallbacks.put(ifaceName, callback); + } catch (RemoteException e) { + loge("setup StaIface failed: " + e.toString()); + return false; } + return true; } @@ -736,6 +793,14 @@ public class SupplicantStaIfaceHal { } } + protected android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + getStaIfaceMockableV1_3(ISupplicantIface iface) { + synchronized (mLock) { + return android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + .asInterface(iface.asBinder()); + } + } + /** * Uses the IServiceManager to check if the device is running V1_1 of the HAL from the VINTF for * the device. @@ -756,6 +821,16 @@ public class SupplicantStaIfaceHal { android.hardware.wifi.supplicant.V1_2.ISupplicant.kInterfaceName); } + /** + * Uses the IServiceManager to check if the device is running V1_3 of the HAL from the VINTF for + * the device. + * @return true if supported, false otherwise. + */ + private boolean isV1_3() { + return checkHalVersionByInterfaceName( + android.hardware.wifi.supplicant.V1_3.ISupplicant.kInterfaceName); + } + private boolean checkHalVersionByInterfaceName(String interfaceName) { if (interfaceName == null) { return false; @@ -795,7 +870,7 @@ public class SupplicantStaIfaceHal { /** * Helper method to look up the network config or the specified iface. */ - private WifiConfiguration getCurrentNetworkLocalConfig(@NonNull String ifaceName) { + protected WifiConfiguration getCurrentNetworkLocalConfig(@NonNull String ifaceName) { return mCurrentNetworkLocalConfigs.get(ifaceName); } @@ -886,7 +961,22 @@ public class SupplicantStaIfaceHal { } SupplicantStaNetworkHal networkHandle = checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork"); - if (networkHandle == null || !networkHandle.select()) { + if (networkHandle == null) { + loge("No valid remote network handle for network configuration: " + + config.configKey()); + return false; + } + + PmkCacheStoreData pmkData = mPmkCacheEntries.get(config.networkId); + if (pmkData != null + && pmkData.expirationTimeInSec > mClock.getElapsedSinceBootMillis() / 1000) { + logi("Set PMK cache for config id " + config.networkId); + if (!networkHandle.setPmkCache(pmkData.data)) { + loge("Set PMK cache failed."); + } + } + + if (!networkHandle.select()) { loge("Failed to select network configuration: " + config.configKey()); return false; } @@ -932,72 +1022,15 @@ public class SupplicantStaIfaceHal { } /** - * Load all the configured networks from wpa_supplicant. - * - * @param ifaceName Name of the interface. - * @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(@NonNull String ifaceName, Map<String, WifiConfiguration> configs, - SparseArray<Map<String, String>> networkExtras) { - synchronized (mLock) { - List<Integer> networkIds = listNetworks(ifaceName); - if (networkIds == null) { - Log.e(TAG, "Failed to list networks"); - return false; - } - for (Integer networkId : networkIds) { - SupplicantStaNetworkHal network = getNetwork(ifaceName, 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(ifaceName, 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|. + * Clean HAL cached data for |networkId| in the framework. * - * @param ifaceName Name of the interface. * @param networkId network id of the network to be removed from supplicant. */ - public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) { + public void removeNetworkCachedData(int networkId) { synchronized (mLock) { - if (getCurrentNetworkId(ifaceName) == networkId) { - // Currently we only save 1 network in supplicant. - removeAllNetworks(ifaceName); + logd("Remove cached HAL data for config id " + networkId); + if (mPmkCacheEntries.remove(networkId) != null) { + updatePmkCacheExpiration(); } } } @@ -1332,6 +1365,23 @@ public class SupplicantStaIfaceHal { } } + private boolean registerCallbackV1_3( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface iface, + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback callback) { + synchronized (mLock) { + String methodStr = "registerCallback_1_3"; + + if (iface == null) return false; + try { + SupplicantStatus status = iface.registerCallback_1_3(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 @@ -2401,7 +2451,7 @@ public class SupplicantStaIfaceHal { /** * Helper function to log callbacks. */ - private void logCallback(final String methodStr) { + protected void logCallback(final String methodStr) { synchronized (mLock) { if (mVerboseLoggingEnabled) { Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received"); @@ -2462,593 +2512,73 @@ public class SupplicantStaIfaceHal { } } - /** - * 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 class SupplicantStaIfaceHalCallback extends ISupplicantStaIfaceCallback.Stub { - private String mIfaceName; - private boolean mStateIsFourway = false; // Used to help check for PSK password mismatch - + protected class SupplicantStaIfaceHalCallback extends SupplicantStaIfaceCallbackImpl { SupplicantStaIfaceHalCallback(@NonNull String ifaceName) { - mIfaceName = ifaceName; - } - - /** - * 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) { - synchronized (mLock) { - try { - return Constants.getANQPElementID(infoID) != null - ? ANQPParser.parseElement( - infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload))) - : ANQPParser.parseHS20Element( - infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload))); - } catch (IOException | BufferUnderflowException e) { - Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e); - return null; - } - } - } - - /** - * 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) { - synchronized (mLock) { - if (payload == null || payload.isEmpty()) return; - ANQPElement element = parseAnqpElement(infoID, payload); - if (element != null) { - elementsMap.put(infoID, element); - } - } - } - - @Override - public void onNetworkAdded(int id) { - synchronized (mLock) { - logCallback("onNetworkAdded"); - } - } - - @Override - public void onNetworkRemoved(int id) { - synchronized (mLock) { - logCallback("onNetworkRemoved"); - // Reset 4way handshake state since network has been removed. - mStateIsFourway = false; - } - } - - @Override - public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, - ArrayList<Byte> ssid) { - synchronized (mLock) { - logCallback("onStateChanged"); - 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(mIfaceName), bssidStr); - } - mWifiMonitor.broadcastSupplicantStateChangeEvent( - mIfaceName, getCurrentNetworkId(mIfaceName), wifiSsid, - bssidStr, newSupplicantState); - } - } - - @Override - public void onAnqpQueryDone(byte[/* 6 */] bssid, - ISupplicantStaIfaceCallback.AnqpData data, - ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) { - synchronized (mLock) { - logCallback("onAnqpQueryDone"); - 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) { - synchronized (mLock) { - logCallback("onHs20IconQueryDone"); - 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) { - synchronized (mLock) { - logCallback("onHs20SubscriptionRemediation"); - mWifiMonitor.broadcastWnmEvent( - mIfaceName, - new WnmData(NativeUtil.macAddressToLong(bssid), url, osuMethod)); - } - } - - @Override - public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode, - int reAuthDelayInSec, String url) { - synchronized (mLock) { - logCallback("onHs20DeauthImminentNotice"); - mWifiMonitor.broadcastWnmEvent( - mIfaceName, - new WnmData(NativeUtil.macAddressToLong(bssid), url, - reasonCode == WnmData.ESS, reAuthDelayInSec)); - } - } - - @Override - public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) { - synchronized (mLock) { - logCallback("onDisconnected"); - if (mVerboseLoggingEnabled) { - Log.e(TAG, "onDisconnected 4way=" + mStateIsFourway - + " locallyGenerated=" + locallyGenerated - + " reasonCode=" + reasonCode); - } - if (mStateIsFourway - && (!locallyGenerated || reasonCode != ReasonCode.IE_IN_4WAY_DIFFERS)) { - mWifiMonitor.broadcastAuthenticationFailureEvent( - mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1); - } - mWifiMonitor.broadcastNetworkDisconnectionEvent( - mIfaceName, locallyGenerated ? 1 : 0, reasonCode, - NativeUtil.macAddressFromByteArray(bssid)); - } - } - - @Override - public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, boolean timedOut) { - synchronized (mLock) { - logCallback("onAssociationRejected"); - - if (statusCode == StatusCode.UNSPECIFIED_FAILURE) { - WifiConfiguration curConfiguration = getCurrentNetworkLocalConfig(mIfaceName); - - if (curConfiguration != null - && curConfiguration.allowedKeyManagement - .get(WifiConfiguration.KeyMgmt.SAE)) { - // Special handling for WPA3-Personal networks. If the password is - // incorrect, the AP will send association rejection, with status code 1 - // (unspecified failure). In SAE networks, the password authentication - // is not related to the 4-way handshake. In this case, we will send an - // authentication failure event up. - logCallback("SAE incorrect password"); - mWifiMonitor.broadcastAuthenticationFailureEvent( - mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1); - } - } - mWifiMonitor.broadcastAssociationRejectionEvent(mIfaceName, statusCode, timedOut, - NativeUtil.macAddressFromByteArray(bssid)); - } - } - - @Override - public void onAuthenticationTimeout(byte[/* 6 */] bssid) { - synchronized (mLock) { - logCallback("onAuthenticationTimeout"); - mWifiMonitor.broadcastAuthenticationFailureEvent( - mIfaceName, WifiManager.ERROR_AUTH_FAILURE_TIMEOUT, -1); - } - } - - @Override - public void onBssidChanged(byte reason, byte[/* 6 */] bssid) { - synchronized (mLock) { - logCallback("onBssidChanged"); - 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() { - synchronized (mLock) { - logCallback("onEapFailure"); - mWifiMonitor.broadcastAuthenticationFailureEvent( - mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, -1); - } - } - - @Override - public void onWpsEventSuccess() { - logCallback("onWpsEventSuccess"); - synchronized (mLock) { - mWifiMonitor.broadcastWpsSuccessEvent(mIfaceName); - } - } - - @Override - public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) { - synchronized (mLock) { - logCallback("onWpsEventFail"); - if (configError == WpsConfigError.MSG_TIMEOUT - && errorInd == WpsErrorIndication.NO_ERROR) { - mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName); - } else { - mWifiMonitor.broadcastWpsFailEvent(mIfaceName, configError, errorInd); - } - } - } - - @Override - public void onWpsEventPbcOverlap() { - synchronized (mLock) { - logCallback("onWpsEventPbcOverlap"); - mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName); - } - } - - @Override - public void onExtRadioWorkStart(int id) { - synchronized (mLock) { - logCallback("onExtRadioWorkStart"); - } - } - - @Override - public void onExtRadioWorkTimeout(int id) { - synchronized (mLock) { - logCallback("onExtRadioWorkTimeout"); - } + super(SupplicantStaIfaceHal.this, ifaceName, mLock, mWifiMonitor); } } - private class SupplicantStaIfaceHalCallbackV1_1 extends - android.hardware.wifi.supplicant.V1_1.ISupplicantStaIfaceCallback.Stub { - private String mIfaceName; - private SupplicantStaIfaceHalCallback mCallbackV1_0; - - SupplicantStaIfaceHalCallbackV1_1(@NonNull String ifaceName, - @NonNull SupplicantStaIfaceHalCallback callback) { - mIfaceName = ifaceName; - mCallbackV1_0 = callback; - } - - @Override - public void onNetworkAdded(int id) { - mCallbackV1_0.onNetworkAdded(id); - } - - @Override - public void onNetworkRemoved(int id) { - mCallbackV1_0.onNetworkRemoved(id); - } - - @Override - public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, - ArrayList<Byte> ssid) { - mCallbackV1_0.onStateChanged(newState, bssid, id, ssid); - } - - @Override - public void onAnqpQueryDone(byte[/* 6 */] bssid, - ISupplicantStaIfaceCallback.AnqpData data, - ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) { - mCallbackV1_0.onAnqpQueryDone(bssid, data, hs20Data); - } - - @Override - public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName, - ArrayList<Byte> data) { - mCallbackV1_0.onHs20IconQueryDone(bssid, fileName, data); - } - - @Override - public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, - byte osuMethod, String url) { - mCallbackV1_0.onHs20SubscriptionRemediation(bssid, osuMethod, url); - } - - @Override - public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode, - int reAuthDelayInSec, String url) { - mCallbackV1_0.onHs20DeauthImminentNotice(bssid, reasonCode, reAuthDelayInSec, url); - } - - @Override - public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, - int reasonCode) { - mCallbackV1_0.onDisconnected(bssid, locallyGenerated, reasonCode); - } - - @Override - public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, - boolean timedOut) { - mCallbackV1_0.onAssociationRejected(bssid, statusCode, timedOut); - } - - @Override - public void onAuthenticationTimeout(byte[/* 6 */] bssid) { - mCallbackV1_0.onAuthenticationTimeout(bssid); - } - - @Override - public void onBssidChanged(byte reason, byte[/* 6 */] bssid) { - mCallbackV1_0.onBssidChanged(reason, bssid); - } - - @Override - public void onEapFailure() { - mCallbackV1_0.onEapFailure(); - } - - @Override - public void onEapFailure_1_1(int code) { - synchronized (mLock) { - logCallback("onEapFailure_1_1"); - mWifiMonitor.broadcastAuthenticationFailureEvent( - mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, code); - } - } - - @Override - public void onWpsEventSuccess() { - mCallbackV1_0.onWpsEventSuccess(); - } - - @Override - public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) { - mCallbackV1_0.onWpsEventFail(bssid, configError, errorInd); - } - - @Override - public void onWpsEventPbcOverlap() { - mCallbackV1_0.onWpsEventPbcOverlap(); - } - - @Override - public void onExtRadioWorkStart(int id) { - mCallbackV1_0.onExtRadioWorkStart(id); - } - - @Override - public void onExtRadioWorkTimeout(int id) { - mCallbackV1_0.onExtRadioWorkTimeout(id); + protected class SupplicantStaIfaceHalCallbackV1_1 extends SupplicantStaIfaceCallbackV1_1Impl { + SupplicantStaIfaceHalCallbackV1_1(@NonNull String ifaceName) { + super(SupplicantStaIfaceHal.this, ifaceName, mLock, mWifiMonitor); } } - private class SupplicantStaIfaceHalCallbackV1_2 extends - android.hardware.wifi.supplicant.V1_2.ISupplicantStaIfaceCallback.Stub { - private SupplicantStaIfaceHalCallbackV1_1 mCallbackV1_1; - - SupplicantStaIfaceHalCallbackV1_2( - @NonNull SupplicantStaIfaceHalCallbackV1_1 callback) { - mCallbackV1_1 = callback; - } - - @Override - public void onNetworkAdded(int id) { - mCallbackV1_1.onNetworkAdded(id); - } - - @Override - public void onNetworkRemoved(int id) { - mCallbackV1_1.onNetworkRemoved(id); - } - - @Override - public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, - ArrayList<Byte> ssid) { - mCallbackV1_1.onStateChanged(newState, bssid, id, ssid); - } - - @Override - public void onAnqpQueryDone(byte[/* 6 */] bssid, - ISupplicantStaIfaceCallback.AnqpData data, - ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) { - mCallbackV1_1.onAnqpQueryDone(bssid, data, hs20Data); - } - - @Override - public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName, - ArrayList<Byte> data) { - mCallbackV1_1.onHs20IconQueryDone(bssid, fileName, data); - } - - @Override - public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, - byte osuMethod, String url) { - mCallbackV1_1.onHs20SubscriptionRemediation(bssid, osuMethod, url); - } - - @Override - public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode, - int reAuthDelayInSec, String url) { - mCallbackV1_1.onHs20DeauthImminentNotice(bssid, reasonCode, reAuthDelayInSec, url); - } - - @Override - public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, - int reasonCode) { - mCallbackV1_1.onDisconnected(bssid, locallyGenerated, reasonCode); - } - - @Override - public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, - boolean timedOut) { - mCallbackV1_1.onAssociationRejected(bssid, statusCode, timedOut); - } - - @Override - public void onAuthenticationTimeout(byte[/* 6 */] bssid) { - mCallbackV1_1.onAuthenticationTimeout(bssid); - } - - @Override - public void onBssidChanged(byte reason, byte[/* 6 */] bssid) { - mCallbackV1_1.onBssidChanged(reason, bssid); - } - - @Override - public void onEapFailure() { - mCallbackV1_1.onEapFailure(); - } - - @Override - public void onEapFailure_1_1(int code) { - mCallbackV1_1.onEapFailure_1_1(code); - } - - @Override - public void onWpsEventSuccess() { - mCallbackV1_1.onWpsEventSuccess(); - } - - @Override - public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) { - mCallbackV1_1.onWpsEventFail(bssid, configError, errorInd); - } - - @Override - public void onWpsEventPbcOverlap() { - mCallbackV1_1.onWpsEventPbcOverlap(); - } - - @Override - public void onExtRadioWorkStart(int id) { - mCallbackV1_1.onExtRadioWorkStart(id); + protected class SupplicantStaIfaceHalCallbackV1_2 extends SupplicantStaIfaceCallbackV1_2Impl { + SupplicantStaIfaceHalCallbackV1_2(@NonNull String ifaceName) { + super(SupplicantStaIfaceHal.this, ifaceName, mContext); } + } - @Override - public void onExtRadioWorkTimeout(int id) { - mCallbackV1_1.onExtRadioWorkTimeout(id); + protected class SupplicantStaIfaceHalCallbackV1_3 extends SupplicantStaIfaceCallbackV1_3Impl { + SupplicantStaIfaceHalCallbackV1_3(@NonNull String ifaceName) { + super(SupplicantStaIfaceHal.this, ifaceName); } + } - @Override - public void onDppSuccessConfigReceived(ArrayList<Byte> ssid, String password, - byte[] psk, int securityAkm) { - if (mDppCallback == null) { - loge("onDppSuccessConfigReceived callback is null"); - return; - } - - WifiConfiguration newWifiConfiguration = new WifiConfiguration(); + protected void addPmkCacheEntry( + int networkId, long expirationTimeInSec, ArrayList<Byte> serializedEntry) { + mPmkCacheEntries.put(networkId, + new PmkCacheStoreData(expirationTimeInSec, serializedEntry)); + updatePmkCacheExpiration(); + } - // Set up SSID - WifiSsid wifiSsid = - WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid)); + private void updatePmkCacheExpiration() { + synchronized (mLock) { + mEventHandler.removeCallbacksAndMessages(PMK_CACHE_EXPIRATION_ALARM_TAG); - newWifiConfiguration.SSID = "\"" + wifiSsid.toString() + "\""; + long elapseTimeInSecond = mClock.getElapsedSinceBootMillis() / 1000; + long nextUpdateTimeInSecond = Long.MAX_VALUE; + logd("Update PMK cache expiration at " + elapseTimeInSecond); - // Set up password or PSK - if (password != null) { - newWifiConfiguration.preSharedKey = "\"" + password + "\""; - } else if (psk != null) { - newWifiConfiguration.preSharedKey = psk.toString(); + Iterator<Map.Entry<Integer, PmkCacheStoreData>> iter = + mPmkCacheEntries.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry<Integer, PmkCacheStoreData> entry = iter.next(); + if (entry.getValue().expirationTimeInSec <= elapseTimeInSecond) { + logd("Config " + entry.getKey() + " PMK is expired."); + iter.remove(); + } else if (entry.getValue().expirationTimeInSec <= 0) { + logd("Config " + entry.getKey() + " PMK expiration time is invalid."); + iter.remove(); + } else if (nextUpdateTimeInSecond > entry.getValue().expirationTimeInSec) { + nextUpdateTimeInSecond = entry.getValue().expirationTimeInSec; + } } - // Set up key management: SAE or PSK - if (securityAkm == DppAkm.SAE || securityAkm == DppAkm.PSK_SAE) { - newWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); - newWifiConfiguration.requirePMF = true; - } else if (securityAkm == DppAkm.PSK) { - newWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); - } else { - // No other AKMs are currently supported - onDppFailure(DppFailureCode.NOT_SUPPORTED); + // No need to arrange next update since there is no valid PMK in the cache. + if (nextUpdateTimeInSecond == Long.MAX_VALUE) { return; } - // Set up default values - newWifiConfiguration.creatorName = mContext.getPackageManager() - .getNameForUid(Process.WIFI_UID); - newWifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); - newWifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); - newWifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); - newWifiConfiguration.status = WifiConfiguration.Status.ENABLED; - - mDppCallback.onSuccessConfigReceived(newWifiConfiguration); - } - - @Override - public void onDppSuccessConfigSent() { - if (mDppCallback != null) { - mDppCallback.onSuccessConfigSent(); - } else { - loge("onSuccessConfigSent callback is null"); - } - } - - @Override - public void onDppProgress(int code) { - if (mDppCallback != null) { - mDppCallback.onProgress(code); - } else { - loge("onDppProgress callback is null"); - } - } - - @Override - public void onDppFailure(int code) { - if (mDppCallback != null) { - mDppCallback.onFailure(code); - } else { - loge("onDppFailure callback is null"); - } + logd("PMK cache next expiration time: " + nextUpdateTimeInSecond); + long delayedTimeInMs = (nextUpdateTimeInSecond - elapseTimeInSecond) * 1000; + mEventHandler.postDelayed( + () -> { + updatePmkCacheExpiration(); + }, + PMK_CACHE_EXPIRATION_ALARM_TAG, + (delayedTimeInMs > 0) ? delayedTimeInMs : 0); } } @@ -3166,6 +2696,66 @@ public class SupplicantStaIfaceHal { return keyMgmtMask.value; } + private @WifiInfo.WifiTechnology int getWifiTechFromCap(ConnectionCapabilities capa) { + switch(capa.technology) { + case WifiTechnology.HE: + return WifiInfo.WIFI_TECHNOLOGY_11AX; + case WifiTechnology.VHT: + return WifiInfo.WIFI_TECHNOLOGY_11AC; + case WifiTechnology.HT: + return WifiInfo.WIFI_TECHNOLOGY_11N; + case WifiTechnology.LEGACY: + return WifiInfo.WIFI_TECHNOLOGY_LEGACY; + default: + return WifiInfo.WIFI_TECHNOLOGY_UNKNOWN; + } + } + + /** + * Returns wifi technology for connected network + * + * This is a v1.3+ HAL feature. + * On error, or if these features are not supported, 0 is returned. + */ + public @WifiInfo.WifiTechnology int getWifiTechnology(@NonNull String ifaceName) { + final String methodStr = "getWifiTechnology"; + MutableInt wifiTechnology = new MutableInt(WifiInfo.WIFI_TECHNOLOGY_UNKNOWN); + + if (isV1_3()) { + ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr); + if (iface == null) { + return WifiInfo.WIFI_TECHNOLOGY_UNKNOWN; + } + + // Get a v1.3 supplicant STA Interface + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 = + getStaIfaceMockableV1_3(iface); + + if (staIfaceV13 == null) { + Log.e(TAG, methodStr + + ": SupplicantStaIface is null, cannot get Connection Capabilities"); + return WifiInfo.WIFI_TECHNOLOGY_UNKNOWN; + } + + try { + staIfaceV13.getConnectionCapabilities( + (SupplicantStatus statusInternal, + ConnectionCapabilities connCapabilitiesInternal) -> { + if (statusInternal.code == SupplicantStatusCode.SUCCESS) { + wifiTechnology.value = getWifiTechFromCap(connCapabilitiesInternal); + } + checkStatusAndLogFailure(statusInternal, methodStr); + }); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } + } else { + Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL"); + } + + return wifiTechnology.value; + } + /** * Adds a DPP peer URI to the URI list. * @@ -3393,4 +2983,8 @@ public class SupplicantStaIfaceHal { public void registerDppCallback(DppEventCallback dppCallback) { mDppCallback = dppCallback; } + + protected DppEventCallback getDppCallback() { + return mDppCallback; + } } diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java index dd56b5f578..bd68f77a3d 100644 --- a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java @@ -22,7 +22,7 @@ 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.HidlSupport.Mutable; +import android.net.wifi.WifiEnterpriseConfig.Ocsp; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; @@ -31,6 +31,7 @@ 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.GeneralUtil.Mutable; import com.android.server.wifi.util.NativeUtil; import org.json.JSONException; @@ -130,6 +131,7 @@ public class SupplicantStaNetworkHal { private boolean mEapEngine; private String mEapEngineID; private String mEapDomainSuffixMatch; + private @Ocsp int mOcsp; SupplicantStaNetworkHal(ISupplicantStaNetwork iSupplicantStaNetwork, String ifaceName, Context context, WifiMonitor monitor) { @@ -159,6 +161,7 @@ public class SupplicantStaNetworkHal { * @return true if succeeds, false otherwise. * @throws IllegalArgumentException on malformed configuration params. */ + @VisibleForTesting public boolean loadWifiConfiguration(WifiConfiguration config, Map<String, String> networkExtras) { synchronized (mLock) { @@ -214,6 +217,12 @@ public class SupplicantStaNetworkHal { config.preSharedKey = NativeUtil.hexStringFromByteArray(mPsk); } /* Do not read SAE password */ + /** SAE Password id */ + config.saePasswordId = null; + if (getSaePasswordId() && !TextUtils.isEmpty(mSaePasswordId)) { + config.saePasswordId = mSaePasswordId; + } + /** allowedKeyManagement */ if (getKeyMgmt()) { BitSet keyMgmtMask = supplicantToWifiConfigurationKeyMgmtMask(mKeyMgmtMask); @@ -313,6 +322,14 @@ public class SupplicantStaNetworkHal { } } + if (config.saePasswordId != null + && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) { + if (!setSaePasswordId(config.saePasswordId)) { + Log.e(TAG, "failed to ste sae password id"); + return false; + } + } + /** Wep Keys */ boolean hasSetKey = false; if (config.wepKeys != null) { @@ -496,6 +513,10 @@ public class SupplicantStaNetworkHal { if (getEapCACert() && !TextUtils.isEmpty(mEapCACert)) { eapConfig.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, mEapCACert); } + /** EAP OCSP type */ + if (getOcsp()) { + eapConfig.setOcsp(mOcsp); + } /** EAP Subject Match */ if (getEapSubjectMatch() && !TextUtils.isEmpty(mEapSubjectMatch)) { eapConfig.setFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, mEapSubjectMatch); @@ -686,6 +707,16 @@ public class SupplicantStaNetworkHal { return false; } + /** + * OCSP (Online Certificate Status Protocol) + * For older HAL compatibility, omit this step to avoid breaking + * connection flow. + */ + if (getV1_3StaNetwork() != null && !setOcsp(eapConfig.getOcsp())) { + Log.e(TAG, "failed to set ocsp"); + return false; + } + return true; } } @@ -696,6 +727,12 @@ public class SupplicantStaNetworkHal { } } + private android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork getV1_3StaNetwork() { + synchronized (mLock) { + return getSupplicantStaNetworkForV1_3Mockable(); + } + } + /** * Maps WifiConfiguration Key Management BitSet to Supplicant HIDL bitmask int * TODO(b/32571829): Update mapping when fast transition keys are added @@ -1462,6 +1499,9 @@ public class SupplicantStaNetworkHal { } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; + } catch (ArrayIndexOutOfBoundsException e) { + Log.e(TAG, "ISupplicantStaNetwork." + methodStr + " failed: " + e); + return false; } } } @@ -1791,6 +1831,39 @@ public class SupplicantStaNetworkHal { } /** See ISupplicantStaNetwork.hal for documentation */ + private boolean getSaePasswordId() { + synchronized (mLock) { + final String methodStr = "getSaePasswordId"; + if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false; + try { + android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork + iSupplicantStaNetworkV12; + + iSupplicantStaNetworkV12 = getV1_2StaNetwork(); + if (iSupplicantStaNetworkV12 != null) { + /* Support for SAE Requires HAL v1.2 or higher */ + MutableBoolean statusOk = new MutableBoolean(false); + iSupplicantStaNetworkV12.getSaePasswordId((SupplicantStatus status, + String saePasswordId) -> { + statusOk.value = status.code == SupplicantStatusCode.SUCCESS; + if (statusOk.value) { + this.mSaePasswordId = saePasswordId; + } else { + checkStatusAndLogFailure(status, methodStr); + } + }); + return statusOk.value; + } else { + return false; + } + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + return false; + } + } + } + + /** See ISupplicantStaNetwork.hal for documentation */ private boolean setSaePasswordId(String saePasswordId) { synchronized (mLock) { final String methodStr = "setSaePasswordId"; @@ -2831,6 +2904,19 @@ public class SupplicantStaNetworkHal { } /** + * Method to mock out the V1_3 ISupplicantStaNetwork retrieval in unit tests. + * + * @return 1.3 ISupplicantStaNetwork object if the device is running the 1.3 supplicant hal + * service, null otherwise. + */ + protected android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + getSupplicantStaNetworkForV1_3Mockable() { + if (mISupplicantStaNetwork == null) return null; + return android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.castFrom( + mISupplicantStaNetwork); + } + + /** * Send eap identity response. * * @param identityStr identity used for EAP-Identity @@ -2883,6 +2969,125 @@ public class SupplicantStaNetworkHal { } } + /** See ISupplicantStaNetwork.hal for documentation */ + private boolean setOcsp(@Ocsp int ocsp) { + synchronized (mLock) { + final String methodStr = "setOcsp"; + if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false; + + int halOcspValue = android.hardware.wifi.supplicant.V1_3.OcspType.NONE; + switch (ocsp) { + case WifiEnterpriseConfig.OCSP_REQUEST_CERT_STATUS: + halOcspValue = android.hardware.wifi.supplicant.V1_3 + .OcspType.REQUEST_CERT_STATUS; + break; + case WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS: + halOcspValue = android.hardware.wifi.supplicant.V1_3 + .OcspType.REQUIRE_CERT_STATUS; + break; + case WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS: + halOcspValue = android.hardware.wifi.supplicant.V1_3 + .OcspType.REQUIRE_ALL_CERTS_STATUS; + break; + } + try { + android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + iSupplicantStaNetworkV13; + + iSupplicantStaNetworkV13 = getV1_3StaNetwork(); + if (iSupplicantStaNetworkV13 != null) { + /* Support for OCSP Requires HAL v1.3 or higher */ + SupplicantStatus status = iSupplicantStaNetworkV13 + .setOcsp(halOcspValue); + return checkStatusAndLogFailure(status, methodStr); + } else { + Log.e(TAG, "Cannot get ISupplicantStaNetwork V1.3"); + return false; + } + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + return false; + } + } + } + + /** See ISupplicantStaNetwork.hal for documentation */ + private boolean getOcsp() { + synchronized (mLock) { + final String methodStr = "getOcsp"; + if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false; + + try { + android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + iSupplicantStaNetworkV13; + iSupplicantStaNetworkV13 = getV1_3StaNetwork(); + if (iSupplicantStaNetworkV13 != null) { + MutableBoolean statusOk = new MutableBoolean(false); + iSupplicantStaNetworkV13.getOcsp((SupplicantStatus status, + int halOcspValue) -> { + statusOk.value = status.code == SupplicantStatusCode.SUCCESS; + if (statusOk.value) { + mOcsp = WifiEnterpriseConfig.OCSP_NONE; + switch (halOcspValue) { + case android.hardware.wifi.supplicant.V1_3 + .OcspType.REQUEST_CERT_STATUS: + mOcsp = WifiEnterpriseConfig.OCSP_REQUEST_CERT_STATUS; + break; + case android.hardware.wifi.supplicant.V1_3 + .OcspType.REQUIRE_CERT_STATUS: + mOcsp = WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS; + break; + case android.hardware.wifi.supplicant.V1_3 + .OcspType.REQUIRE_ALL_CERTS_STATUS: + mOcsp = WifiEnterpriseConfig + .OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS; + break; + default: + Log.e(TAG, "Invalid HAL OCSP value " + halOcspValue); + break; + } + } else { + checkStatusAndLogFailure(status, methodStr); + } + }); + return statusOk.value; + } else { + Log.e(TAG, "Cannot get ISupplicantStaNetwork V1.3"); + return false; + } + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + return false; + } + } + } + + /** See ISupplicantStaNetwork.hal for documentation */ + public boolean setPmkCache(ArrayList<Byte> serializedEntry) { + synchronized (mLock) { + final String methodStr = "setPmkCache"; + if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false; + + try { + android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + iSupplicantStaNetworkV13; + + iSupplicantStaNetworkV13 = getV1_3StaNetwork(); + if (iSupplicantStaNetworkV13 != null) { + SupplicantStatus status = iSupplicantStaNetworkV13 + .setPmkCache(serializedEntry); + return checkStatusAndLogFailure(status, methodStr); + } else { + Log.e(TAG, "Cannot get ISupplicantStaNetwork V1.3"); + return false; + } + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + return false; + } + } + } + /** * Retrieve the NFC token for this network. * diff --git a/service/java/com/android/server/wifi/SupplicantStateTracker.java b/service/java/com/android/server/wifi/SupplicantStateTracker.java index c60715b6d5..11148c5ffe 100644 --- a/service/java/com/android/server/wifi/SupplicantStateTracker.java +++ b/service/java/com/android/server/wifi/SupplicantStateTracker.java @@ -28,7 +28,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; -import android.util.Slog; import com.android.internal.app.IBatteryStats; import com.android.internal.util.State; @@ -69,9 +68,6 @@ public class SupplicantStateTracker extends StateMachine { /* Maximum retries on assoc rejection events */ private static final int MAX_RETRIES_ON_ASSOCIATION_REJECT = 16; - /* Tracks if networks have been disabled during a connection */ - private boolean mNetworksDisabledDuringConnect = false; - private final Context mContext; private final State mUninitializedState = new UninitializedState(); @@ -126,13 +122,9 @@ public class SupplicantStateTracker extends StateMachine { private void handleNetworkConnectionFailure(int netId, int disableReason) { if (DBG) { Log.d(TAG, "handleNetworkConnectionFailure netId=" + Integer.toString(netId) - + " reason " + Integer.toString(disableReason) - + " mNetworksDisabledDuringConnect=" + mNetworksDisabledDuringConnect); + + " reason " + Integer.toString(disableReason)); } - /* If other networks disabled during connection, enable them */ - if (mNetworksDisabledDuringConnect) { - mNetworksDisabledDuringConnect = false; } /* update network status */ mWifiConfigManager.updateNetworkSelectionStatus(netId, disableReason); } @@ -202,7 +194,7 @@ public class SupplicantStateTracker extends StateMachine { case UNINITIALIZED: supplState = BatteryStats.WIFI_SUPPL_STATE_UNINITIALIZED; break; case INVALID: supplState = BatteryStats.WIFI_SUPPL_STATE_INVALID; break; default: - Slog.w(TAG, "Unknown supplicant state " + state); + Log.w(TAG, "Unknown supplicant state " + state); supplState = BatteryStats.WIFI_SUPPL_STATE_INVALID; break; } @@ -255,9 +247,6 @@ public class SupplicantStateTracker extends StateMachine { case ClientModeImpl.CMD_RESET_SUPPLICANT_STATE: transitionTo(mUninitializedState); break; - case WifiManager.CONNECT_NETWORK: - mNetworksDisabledDuringConnect = true; - break; case WifiMonitor.ASSOCIATION_REJECTION_EVENT: default: Log.e(TAG, "Ignoring " + message); @@ -368,10 +357,6 @@ public class SupplicantStateTracker extends StateMachine { @Override public void enter() { if (DBG) Log.d(TAG, getName() + "\n"); - /* Reset authentication failure count */ - if (mNetworksDisabledDuringConnect) { - mNetworksDisabledDuringConnect = false; - } } @Override public boolean processMessage(Message message) { @@ -411,7 +396,6 @@ public class SupplicantStateTracker extends StateMachine { 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/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java index 1b5ccd3e3c..bf36c94821 100644 --- a/service/java/com/android/server/wifi/WakeupController.java +++ b/service/java/com/android/server/wifi/WakeupController.java @@ -16,8 +16,6 @@ package com.android.server.wifi; -import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; - import android.content.Context; import android.database.ContentObserver; import android.net.wifi.ScanResult; @@ -25,7 +23,6 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiScanner; import android.os.Handler; -import android.os.Looper; import android.os.Process; import android.provider.Settings; import android.util.Log; @@ -128,7 +125,7 @@ public class WakeupController { public WakeupController( Context context, - Looper looper, + Handler handler, WakeupLock wakeupLock, WakeupEvaluator wakeupEvaluator, WakeupOnboarding wakeupOnboarding, @@ -140,7 +137,7 @@ public class WakeupController { FrameworkFacade frameworkFacade, Clock clock) { mContext = context; - mHandler = new Handler(looper); + mHandler = handler; mWakeupLock = wakeupLock; mWakeupEvaluator = wakeupEvaluator; mWakeupOnboarding = wakeupOnboarding; @@ -404,14 +401,14 @@ public class WakeupController { /** * Enables wifi. * - * <p>This method ignores all checks and assumes that {@link WifiController} is currently + * <p>This method ignores all checks and assumes that {@link ActiveModeWarden} is currently * in ScanModeState. */ private void enableWifi() { if (USE_PLATFORM_WIFI_WAKE) { // TODO(b/72180295): ensure that there is no race condition with WifiServiceImpl here if (mWifiInjector.getWifiSettingsStore().handleWifiToggled(true /* wifiEnabled */)) { - mWifiInjector.getWifiController().sendMessage(CMD_WIFI_TOGGLED); + mWifiInjector.getActiveModeWarden().wifiToggled(); mWifiWakeMetrics.recordWakeupEvent(mNumScansHandled); } } diff --git a/service/java/com/android/server/wifi/WakeupNotificationFactory.java b/service/java/com/android/server/wifi/WakeupNotificationFactory.java index 23f31a7db4..748a74d6ce 100644 --- a/service/java/com/android/server/wifi/WakeupNotificationFactory.java +++ b/service/java/com/android/server/wifi/WakeupNotificationFactory.java @@ -42,10 +42,13 @@ public class WakeupNotificationFactory { public static final int ONBOARD_ID = SystemMessage.NOTE_WIFI_WAKE_ONBOARD; private final Context mContext; + private final WifiInjector mWifiInjector; private final FrameworkFacade mFrameworkFacade; - WakeupNotificationFactory(Context context, FrameworkFacade frameworkFacade) { + WakeupNotificationFactory(Context context, WifiInjector wifiInjector, + FrameworkFacade frameworkFacade) { mContext = context; + mWifiInjector = wifiInjector; mFrameworkFacade = frameworkFacade; } @@ -80,7 +83,8 @@ public class WakeupNotificationFactory { private PendingIntent getPrivateBroadcast(String action) { - Intent intent = new Intent(action).setPackage("android"); + Intent intent = new Intent(action) + .setPackage(mWifiInjector.getWifiStackPackageName()); return mFrameworkFacade.getBroadcast( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } diff --git a/service/java/com/android/server/wifi/WakeupOnboarding.java b/service/java/com/android/server/wifi/WakeupOnboarding.java index b6bcbc3c0e..93540e00ac 100644 --- a/service/java/com/android/server/wifi/WakeupOnboarding.java +++ b/service/java/com/android/server/wifi/WakeupOnboarding.java @@ -26,7 +26,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; -import android.os.Looper; import android.os.SystemClock; import android.provider.Settings; import android.text.format.DateUtils; @@ -93,12 +92,12 @@ public class WakeupOnboarding { public WakeupOnboarding( Context context, WifiConfigManager wifiConfigManager, - Looper looper, + Handler handler, FrameworkFacade frameworkFacade, WakeupNotificationFactory wakeupNotificationFactory) { mContext = context; mWifiConfigManager = wifiConfigManager; - mHandler = new Handler(looper); + mHandler = handler; mFrameworkFacade = frameworkFacade; mWakeupNotificationFactory = wakeupNotificationFactory; diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index 7970ab72f9..87a170c785 100644 --- a/service/java/com/android/server/wifi/WifiApConfigStore.java +++ b/service/java/com/android/server/wifi/WifiApConfigStore.java @@ -28,7 +28,6 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.Environment; import android.os.Handler; -import android.os.Looper; import android.text.TextUtils; import android.util.Log; @@ -45,9 +44,9 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Random; -import java.util.UUID; /** * Provides API for reading/writing soft access point configuration. @@ -85,24 +84,28 @@ public class WifiApConfigStore { private ArrayList<Integer> mAllowed2GChannel = null; private final Context mContext; + private final WifiInjector mWifiInjector; private final Handler mHandler; private final String mApConfigFile; private final BackupManagerProxy mBackupManagerProxy; private final FrameworkFacade mFrameworkFacade; private boolean mRequiresApBandConversion = false; - WifiApConfigStore(Context context, Looper looper, + WifiApConfigStore(Context context, WifiInjector wifiInjector, Handler handler, BackupManagerProxy backupManagerProxy, FrameworkFacade frameworkFacade) { - this(context, looper, backupManagerProxy, frameworkFacade, DEFAULT_AP_CONFIG_FILE); + this(context, wifiInjector, handler, backupManagerProxy, frameworkFacade, + DEFAULT_AP_CONFIG_FILE); } WifiApConfigStore(Context context, - Looper looper, + WifiInjector wifiInjector, + Handler handler, BackupManagerProxy backupManagerProxy, FrameworkFacade frameworkFacade, String apConfigFile) { mContext = context; - mHandler = new Handler(looper); + mWifiInjector = wifiInjector; + mHandler = handler; mBackupManagerProxy = backupManagerProxy; mFrameworkFacade = frameworkFacade; mApConfigFile = apConfigFile; @@ -173,7 +176,7 @@ public class WifiApConfigStore { * Update the current soft access point configuration. * Restore to default AP configuration if null is provided. * This can be invoked under context of binder threads (WifiManager.setWifiApConfiguration) - * and ClientModeImpl thread (CMD_START_AP). + * and the main Wifi thread (CMD_START_AP). */ public synchronized void setApConfiguration(WifiConfiguration config) { if (config == null) { @@ -340,9 +343,7 @@ public class WifiApConfigStore { config.SSID = mContext.getResources().getString( 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); + config.preSharedKey = generatePassword(); return config; } @@ -364,9 +365,7 @@ public class WifiApConfigStore { config.apBand = apBand; 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); + config.preSharedKey = generatePassword(); return config; } @@ -496,8 +495,23 @@ public class WifiApConfigStore { } private PendingIntent getPrivateBroadcast(String action) { - Intent intent = new Intent(action).setPackage("android"); + Intent intent = new Intent(action) + .setPackage(mWifiInjector.getWifiStackPackageName()); return mFrameworkFacade.getBroadcast( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } + + private static String generatePassword() { + // Characters that will be used for password generation. Some characters commonly known to + // be confusing like 0 and O excluded from this list. + final String allowed = "23456789abcdefghijkmnpqrstuvwxyz"; + final int passLength = 15; + + StringBuilder sb = new StringBuilder(passLength); + SecureRandom random = new SecureRandom(); + for (int i = 0; i < passLength; i++) { + sb.append(allowed.charAt(random.nextInt(allowed.length()))); + } + return sb.toString(); + } } diff --git a/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java b/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java index 742cc99d83..9843f0d95a 100644 --- a/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java +++ b/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java @@ -502,9 +502,8 @@ class WifiBackupDataV1Parser implements WifiBackupDataParser { } } if (gatewayAddressString != null) { - LinkAddress dest = null; InetAddress gateway = NetworkUtils.numericToInetAddress(gatewayAddressString); - RouteInfo route = new RouteInfo(dest, gateway); + RouteInfo route = new RouteInfo(null, gateway, null, RouteInfo.RTN_UNICAST); if (route.isIPv4Default()) { staticIpConfiguration.gateway = gateway; } else { diff --git a/service/java/com/android/server/wifi/WifiCandidates.java b/service/java/com/android/server/wifi/WifiCandidates.java index ee5bd30bfe..ec34b6ece7 100644 --- a/service/java/com/android/server/wifi/WifiCandidates.java +++ b/service/java/com/android/server/wifi/WifiCandidates.java @@ -77,6 +77,10 @@ public class WifiCandidates { */ boolean isTrusted(); /** + * Returns true for a metered network. + */ + boolean isMetered(); + /** * Returns the ID of the evaluator that provided the candidate. */ @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int getEvaluatorId(); @@ -132,6 +136,7 @@ public class WifiCandidates { private WifiScoreCard.PerBssid mPerBssid; // For accessing the scorecard entry private final boolean mIsCurrentNetwork; private final boolean mIsCurrentBssid; + private final boolean mIsMetered; CandidateImpl(Key key, ScanDetail scanDetail, @@ -141,7 +146,8 @@ public class WifiCandidates { WifiScoreCard.PerBssid perBssid, double lastSelectionWeight, boolean isCurrentNetwork, - boolean isCurrentBssid) { + boolean isCurrentBssid, + boolean isMetered) { this.key = key; this.scanDetail = scanDetail; this.config = config; @@ -151,6 +157,7 @@ public class WifiCandidates { this.lastSelectionWeight = lastSelectionWeight; this.mIsCurrentNetwork = isCurrentNetwork; this.mIsCurrentBssid = isCurrentBssid; + this.mIsMetered = isMetered; } @Override @@ -190,6 +197,11 @@ public class WifiCandidates { } @Override + public boolean isMetered() { + return (mIsMetered); + } + + @Override public @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int getEvaluatorId() { return evaluatorId; } @@ -248,17 +260,10 @@ public class WifiCandidates { String getIdentifier(); /** - * Calculates the score for a group of candidates that belong - * to the same network. + * Calculates the best score for a collection of candidates. */ - @Nullable ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> group); + @Nullable ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> candidates); - /** - * Returns true if the legacy user connect choice logic should be used. - * - * @returns false to disable the legacy logic - */ - boolean userConnectChoiceOverrideWanted(); } /** @@ -275,16 +280,20 @@ public class WifiCandidates { public final double value; public final double err; public final Key candidateKey; - public ScoredCandidate(double value, double err, Candidate candidate) { + public final boolean userConnectChoiceOverride; + public ScoredCandidate(double value, double err, boolean userConnectChoiceOverride, + Candidate candidate) { this.value = value; this.err = err; this.candidateKey = (candidate == null) ? null : candidate.getKey(); + this.userConnectChoiceOverride = userConnectChoiceOverride; } /** * Represents no score */ public static final ScoredCandidate NONE = - new ScoredCandidate(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, null); + new ScoredCandidate(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, + false, null); } /** @@ -348,7 +357,8 @@ public class WifiCandidates { WifiConfiguration config, @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int evaluatorId, int evaluatorScore, - double lastSelectionWeightBetweenZeroAndOne) { + double lastSelectionWeightBetweenZeroAndOne, + boolean isMetered) { if (config == null) return failure(); if (scanDetail == null) return failure(); ScanResult scanResult = scanDetail.getScanResult(); @@ -381,17 +391,11 @@ public class WifiCandidates { scanDetail, config, evaluatorId, evaluatorScore, perBssid, Math.min(Math.max(lastSelectionWeightBetweenZeroAndOne, 0.0), 1.0), config.networkId == mCurrentNetworkId, - bssid.equals(mCurrentBssid)); + bssid.equals(mCurrentBssid), + isMetered); mCandidates.put(key, candidate); return true; } - /** Adds a new candidate with no user selection weight. */ - public boolean add(ScanDetail scanDetail, - WifiConfiguration config, - @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int evaluatorId, - int evaluatorScore) { - return add(scanDetail, config, evaluatorId, evaluatorScore, 0.0); - } /** * Removes a candidate @@ -399,7 +403,7 @@ public class WifiCandidates { */ public boolean remove(Candidate candidate) { if (!(candidate instanceof CandidateImpl)) return failure(); - return mCandidates.remove(((CandidateImpl) candidate).key, (CandidateImpl) candidate); + return mCandidates.remove(((CandidateImpl) candidate).key, candidate); } /** @@ -432,14 +436,9 @@ public class WifiCandidates { */ public @NonNull ScoredCandidate choose(@NonNull CandidateScorer candidateScorer) { Preconditions.checkNotNull(candidateScorer); - ScoredCandidate choice = ScoredCandidate.NONE; - for (Collection<Candidate> group : getGroupedCandidates()) { - ScoredCandidate scoredCandidate = candidateScorer.scoreCandidates(group); - if (scoredCandidate != null && scoredCandidate.value > choice.value) { - choice = scoredCandidate; - } - } - return choice; + Collection<Candidate> candidates = new ArrayList<>(mCandidates.values()); + ScoredCandidate choice = candidateScorer.scoreCandidates(candidates); + return choice == null ? ScoredCandidate.NONE : choice; } /** diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 683ace4c83..a0a032d540 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -16,10 +16,9 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.admin.DeviceAdminInfo; -import android.app.admin.DevicePolicyManagerInternal; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -38,7 +37,6 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; import android.os.Handler; -import android.os.Looper; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; @@ -76,6 +74,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.crypto.Mac; + /** * This class provides the APIs to manage configured Wi-Fi networks. * It deals with the following: @@ -95,7 +95,7 @@ import java.util.Set; * in the internal database. Any configuration updates should be triggered with appropriate helper * methods of this class using the configuration's unique networkId. * - * NOTE: These API's are not thread safe and should only be used from ClientModeImpl thread. + * NOTE: These API's are not thread safe and should only be used from the main Wifi thread. */ public class WifiConfigManager { /** @@ -158,34 +158,34 @@ public class WifiConfigManager { Integer.MAX_VALUE // threshold for DISABLED_AUTHENTICATION_NO_SUBSCRIBED }; /** - * Interface for other modules to listen to the saved network updated + * Interface for other modules to listen to the network updated * events. */ - public interface OnSavedNetworkUpdateListener { + public interface OnNetworkUpdateListener { /** - * Invoked on saved network being added. + * Invoked on network being added. */ - void onSavedNetworkAdded(int networkId); + void onNetworkAdded(@NonNull WifiConfiguration config); /** - * Invoked on saved network being enabled. + * Invoked on network being enabled. */ - void onSavedNetworkEnabled(int networkId); + void onNetworkEnabled(@NonNull WifiConfiguration config); /** - * Invoked on saved network being permanently disabled. + * Invoked on network being permanently disabled. */ - void onSavedNetworkPermanentlyDisabled(int networkId, int disableReason); + void onNetworkPermanentlyDisabled(@NonNull WifiConfiguration config, int disableReason); /** - * Invoked on saved network being removed. + * Invoked on network being removed. */ - void onSavedNetworkRemoved(int networkId); + void onNetworkRemoved(@NonNull WifiConfiguration config); /** - * Invoked on saved network being temporarily disabled. + * Invoked on network being temporarily disabled. */ - void onSavedNetworkTemporarilyDisabled(int networkId, int disableReason); + void onNetworkTemporarilyDisabled(@NonNull WifiConfiguration config, int disableReason); /** - * Invoked on saved network being updated. + * Invoked on network being updated. */ - void onSavedNetworkUpdated(int networkId); + void onNetworkUpdated(@NonNull WifiConfiguration config); } /** * Max size of scan details to cache in {@link #mScanDetailCaches}. @@ -228,6 +228,12 @@ public class WifiConfigManager { private static final int WIFI_PNO_FREQUENCY_CULLING_ENABLED_DEFAULT = 1; // 0 = disabled private static final int WIFI_PNO_RECENCY_SORTING_ENABLED_DEFAULT = 1; // 0 = disabled: + @VisibleForTesting + protected static final long AGGRESSIVE_MAC_REFRESH_MS = 10 * 60 * 1000; //10 minutes + + private static final MacAddress DEFAULT_MAC_ADDRESS = + MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); + /** * Expiration timeout for deleted ephemeral ssids. (1 day) */ @@ -272,12 +278,12 @@ public class WifiConfigManager { private final WifiPermissionsWrapper mWifiPermissionsWrapper; private final WifiInjector mWifiInjector; private boolean mConnectedMacRandomzationSupported; + private final Mac mMac; /** * Local log used for debugging any WifiConfigManager issues. */ - private final LocalLog mLocalLog = - new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256); + private final LocalLog mLocalLog; /** * Map of configured networks with network id as the key. */ @@ -303,6 +309,13 @@ public class WifiConfigManager { */ private final Map<String, String> mRandomizedMacAddressMapping; + private final Set<String> mAggressiveMacRandomizationWhitelist; + private final Set<String> mAggressiveMacRandomizationBlacklist; + + /** + * Store the network update listeners. + */ + private final List<OnNetworkUpdateListener> mListeners; /** * Flag to indicate if only networks with the same psk should be linked. * TODO(b/30706406): Remove this flag if unused. @@ -314,6 +327,7 @@ public class WifiConfigManager { private final int mMaxNumActiveChannelsForPartialScans; private final FrameworkFacade mFrameworkFacade; + private final DeviceConfigFacade mDeviceConfigFacade; /** * Verbose logging flag. Toggled by developer options. @@ -366,14 +380,10 @@ public class WifiConfigManager { private final DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData; private final RandomizedMacStoreData mRandomizedMacStoreData; - // Store the saved network update listener. - private OnSavedNetworkUpdateListener mListener = null; private boolean mPnoFrequencyCullingEnabled = false; private boolean mPnoRecencySortingEnabled = false; - - /** * Create new instance of WifiConfigManager. */ @@ -388,7 +398,8 @@ public class WifiConfigManager { NetworkListUserStoreData networkListUserStoreData, DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData, RandomizedMacStoreData randomizedMacStoreData, - FrameworkFacade frameworkFacade, Looper looper) { + FrameworkFacade frameworkFacade, Handler handler, + DeviceConfigFacade deviceConfigFacade) { mContext = context; mClock = clock; mUserManager = userManager; @@ -404,6 +415,7 @@ public class WifiConfigManager { mScanDetailCaches = new HashMap<>(16, 0.75f); mDeletedEphemeralSsidsToTimeMap = new HashMap<>(); mRandomizedMacAddressMapping = new HashMap<>(); + mListeners = new ArrayList<>(); // Register store data for network list and deleted ephemeral SSIDs. mNetworkListSharedStoreData = networkListSharedStoreData; @@ -422,7 +434,7 @@ public class WifiConfigManager { mFrameworkFacade = frameworkFacade; mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor( Settings.Global.WIFI_PNO_FREQUENCY_CULLING_ENABLED), false, - new ContentObserver(new Handler(looper)) { + new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { updatePnoFrequencyCullingSetting(); @@ -431,7 +443,7 @@ public class WifiConfigManager { updatePnoFrequencyCullingSetting(); mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor( Settings.Global.WIFI_PNO_RECENCY_SORTING_ENABLED), false, - new ContentObserver(new Handler(looper)) { + new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { updatePnoRecencySortingSetting(); @@ -440,12 +452,27 @@ public class WifiConfigManager { updatePnoRecencySortingSetting(); mConnectedMacRandomzationSupported = mContext.getResources() .getBoolean(R.bool.config_wifi_connected_mac_randomization_supported); + mDeviceConfigFacade = deviceConfigFacade; + mAggressiveMacRandomizationWhitelist = new ArraySet<>(); + mAggressiveMacRandomizationBlacklist = new ArraySet<>(); + + mLocalLog = new LocalLog( + context.getSystemService(ActivityManager.class).isLowRamDevice() ? 128 : 256); + try { - mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser(SYSUI_PACKAGE_NAME, - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); + // TODO(b/141890172): do not hardcode SYSUI_PACKAGE_NAME + mSystemUiUid = mContext + .createPackageContextAsUser(SYSUI_PACKAGE_NAME, 0, UserHandle.SYSTEM) + .getPackageManager() + .getPackageUid(SYSUI_PACKAGE_NAME, PackageManager.MATCH_SYSTEM_ONLY); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Unable to resolve SystemUI's UID."); } + mMac = WifiConfigurationUtil.obtainMacRandHashFunction(Process.WIFI_UID); + if (mMac == null) { + Log.wtf(TAG, "Failed to obtain secret for MAC randomization." + + " All randomized MAC addresses are lost!"); + } } /** @@ -465,6 +492,136 @@ public class WifiConfigManager { } /** + * Determine if the framework should perform "aggressive" MAC randomization when connecting + * to the SSID in the input WifiConfiguration. + * @param config + * @return + */ + private boolean shouldUseAggressiveRandomization(WifiConfiguration config) { + if (mDeviceConfigFacade.isAggressiveMacRandomizationSsidWhitelistEnabled()) { + return isSsidOptInForAggressiveRandomization(config.SSID); + } + return false; + } + + private boolean isSsidOptInForAggressiveRandomization(String ssid) { + if (mAggressiveMacRandomizationBlacklist.contains(ssid)) { + return false; + } + return mAggressiveMacRandomizationWhitelist.contains(ssid); + } + + /** + * Sets the list of SSIDs that the framework should perform aggressive MAC randomization on. + * @param whitelist + */ + public void setAggressiveMacRandomizationWhitelist(Set<String> whitelist) { + // TODO: b/137795359 persist this with WifiConfigStore + mAggressiveMacRandomizationWhitelist.clear(); + mAggressiveMacRandomizationWhitelist.addAll(whitelist); + } + + /** + * Sets the list of SSIDs that the framework will never perform aggressive MAC randomization + * on. + * @param blacklist + */ + public void setAggressiveMacRandomizationBlacklist(Set<String> blacklist) { + mAggressiveMacRandomizationBlacklist.clear(); + mAggressiveMacRandomizationBlacklist.addAll(blacklist); + } + + @VisibleForTesting + protected int getRandomizedMacAddressMappingSize() { + return mRandomizedMacAddressMapping.size(); + } + + /** + * The persistent randomized MAC address is locally generated for each SSID and does not + * change until factory reset of the device. In the initial Q release the per-SSID randomized + * MAC is saved on the device, but in an update the storing of randomized MAC is removed. + * Instead, the randomized MAC is calculated directly from the SSID and a on device secret. + * For backward compatibility, this method first checks the device storage for saved + * randomized MAC. If it is not found or the saved MAC is invalid then it will calculate the + * randomized MAC directly. + * + * In the future as devices launched on Q no longer get supported, this method should get + * simplified to return the calculated MAC address directly. + * @param config the WifiConfiguration to obtain MAC address for. + * @return persistent MAC address for this WifiConfiguration + */ + private MacAddress getPersistentMacAddress(WifiConfiguration config) { + // mRandomizedMacAddressMapping had been the location to save randomized MAC addresses. + String persistentMacString = mRandomizedMacAddressMapping.get( + config.getSsidAndSecurityTypeString()); + // Use the MAC address stored in the storage if it exists and is valid. Otherwise + // use the MAC address calculated from a hash function as the persistent MAC. + if (persistentMacString != null) { + try { + return MacAddress.fromString(persistentMacString); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Error creating randomized MAC address from stored value."); + mRandomizedMacAddressMapping.remove(config.getSsidAndSecurityTypeString()); + } + } + return WifiConfigurationUtil.calculatePersistentMacForConfiguration(config, mMac); + } + + /** + * Obtain the persistent MAC address by first reading from an internal database. If non exists + * then calculate the persistent MAC using HMAC-SHA256. + * Finally set the randomized MAC of the configuration to the randomized MAC obtained. + * @param config the WifiConfiguration to make the update + * @return the persistent MacAddress or null if the operation is unsuccessful + */ + private MacAddress setRandomizedMacToPersistentMac(WifiConfiguration config) { + MacAddress persistentMac = getPersistentMacAddress(config); + if (persistentMac == null || persistentMac.equals(config.getRandomizedMacAddress())) { + return persistentMac; + } + WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId); + internalConfig.setRandomizedMacAddress(persistentMac); + internalConfig.randomizedMacLastModifiedTimeMs = mClock.getWallClockMillis(); + return persistentMac; + } + + /** + * Re-randomizes the randomized MAC address if needed. + * @param config the WifiConfiguration to make the update + * @return the updated MacAddress + */ + private MacAddress updateRandomizedMacIfNeeded(WifiConfiguration config) { + boolean shouldUpdateMac = config.randomizedMacLastModifiedTimeMs + + AGGRESSIVE_MAC_REFRESH_MS + < mClock.getWallClockMillis(); + if (!shouldUpdateMac) { + return config.getRandomizedMacAddress(); + } + WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId); + internalConfig.setRandomizedMacAddress(MacAddress.createRandomUnicastAddress()); + internalConfig.randomizedMacLastModifiedTimeMs = mClock.getWallClockMillis(); + return internalConfig.getRandomizedMacAddress(); + } + + /** + * Returns the randomized MAC address that should be used for this WifiConfiguration. + * This API may return a randomized MAC different from the persistent randomized MAC if + * the WifiConfiguration is configured for aggressive MAC randomization. + * @param config + * @return MacAddress + */ + public MacAddress getRandomizedMacAndUpdateIfNeeded(WifiConfiguration config) { + MacAddress mac; + if (!config.getNetworkSelectionStatus().getHasEverConnected() + || !shouldUseAggressiveRandomization(config)) { + mac = setRandomizedMacToPersistentMac(config); + } else { + mac = updateRandomizedMacIfNeeded(config); + } + return mac; + } + + /** * Enable/disable verbose logging in WifiConfigManager & its helper classes. */ public void enableVerboseLogging(int verbose) { @@ -508,7 +665,8 @@ public class WifiConfigManager { } } } - if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) { + if (configuration.enterpriseConfig != null && !TextUtils.isEmpty( + configuration.enterpriseConfig.getPassword())) { configuration.enterpriseConfig.setPassword(PASSWORD_MASK); } } @@ -520,8 +678,7 @@ public class WifiConfigManager { * @param configuration WifiConfiguration to hide the MAC address */ private void maskRandomizedMacAddressInWifiConfiguration(WifiConfiguration configuration) { - MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); - configuration.setRandomizedMacAddress(defaultMac); + configuration.setRandomizedMacAddress(DEFAULT_MAC_ADDRESS); } /** @@ -770,8 +927,10 @@ public class WifiConfigManager { * * @param config WifiConfiguration object corresponding to the network to be modified. * @param uid UID of the app requesting the modification. + * @param packageName Package name of the app requesting the modification. */ - private boolean canModifyNetwork(WifiConfiguration config, int uid) { + private boolean canModifyNetwork(WifiConfiguration config, int uid, + @Nullable String packageName) { // System internals can always update networks; they're typically only // making meteredHint or meteredOverride changes if (uid == Process.SYSTEM_UID) { @@ -795,31 +954,21 @@ public class WifiConfigManager { return true; } - final DevicePolicyManagerInternal dpmi = - mWifiPermissionsWrapper.getDevicePolicyManagerInternal(); - - final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, - DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + final boolean isDeviceOwner = mWifiPermissionsUtil.isDeviceOwner(uid, packageName); // If |uid| corresponds to the device owner, allow all modifications. - if (isUidDeviceOwner) { + if (isDeviceOwner) { return true; } final boolean isCreator = (config.creatorUid == uid); - // Check if device has DPM capability. If it has and |dpmi| is still null, then we - // treat this case with suspicion and bail out. - if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) - && dpmi == null) { - Log.w(TAG, "Error retrieving DPMI service."); - return false; - } - // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner. - final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy( - config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + final boolean isConfigEligibleForLockdown = + mWifiPermissionsUtil.isDeviceOwner(config.creatorUid, config.creatorName); if (!isConfigEligibleForLockdown) { + // App that created the network or settings app (i.e user) has permission to + // modify the network. return isCreator || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid); } @@ -827,6 +976,7 @@ public class WifiConfigManager { final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver, Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0; return !isLockdownFeatureEnabled + // If not locked down, settings app (i.e user) has permission to modify the network. && mWifiPermissionsUtil.checkNetworkSettingsPermission(uid); } @@ -846,8 +996,10 @@ public class WifiConfigManager { if (uid == android.os.Process.SYSTEM_UID || uid == mSystemUiUid) { return true; } else { - return WifiConfigurationUtil.doesUidBelongToAnyProfile( - uid, mUserManager.getProfiles(mCurrentUserId)); + UserHandle currentUser = UserHandle.of(mCurrentUserId); + UserHandle callingUser = UserHandle.getUserHandleForUid(uid); + return currentUser.equals(callingUser) + || mUserManager.isSameProfileGroup(currentUser, callingUser); } } @@ -882,6 +1034,7 @@ public class WifiConfigManager { && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) { internalConfig.preSharedKey = externalConfig.preSharedKey; } + internalConfig.saePasswordId = externalConfig.saePasswordId; // 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. @@ -1050,34 +1203,11 @@ public class WifiConfigManager { packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid); newInternalConfig.creationTime = newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis()); - updateRandomizedMacAddress(newInternalConfig); - - return newInternalConfig; - } - - /** - * Sets the randomized address for the given configuration from stored map if it exist. - * Otherwise generates a new randomized address and save to the stored map. - * @param config - */ - private void updateRandomizedMacAddress(WifiConfiguration config) { - // Update randomized MAC address according to stored map - final String key = config.getSsidAndSecurityTypeString(); - // If the key is not found in the current store, then it means this network has never been - // seen before. So add it to store. - if (!mRandomizedMacAddressMapping.containsKey(key)) { - mRandomizedMacAddressMapping.put(key, - config.getOrCreateRandomizedMacAddress().toString()); - } else { // Otherwise read from the store and set the WifiConfiguration - try { - config.setRandomizedMacAddress( - MacAddress.fromString(mRandomizedMacAddressMapping.get(key))); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Error creating randomized MAC address from stored value."); - mRandomizedMacAddressMapping.put(key, - config.getOrCreateRandomizedMacAddress().toString()); - } + MacAddress randomizedMac = getPersistentMacAddress(newInternalConfig); + if (randomizedMac != null) { + newInternalConfig.setRandomizedMacAddress(randomizedMac); } + return newInternalConfig; } /** @@ -1146,7 +1276,7 @@ public class WifiConfigManager { return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); } // Check for the app's permission before we let it update this network. - if (!canModifyNetwork(existingInternalConfig, uid)) { + if (!canModifyNetwork(existingInternalConfig, uid, packageName)) { Log.e(TAG, "UID " + uid + " does not have permission to update configuration " + config.configKey()); return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); @@ -1158,7 +1288,7 @@ public class WifiConfigManager { // Only add networks with proxy settings if the user has permission to if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig) - && !canModifyProxySettings(uid)) { + && !canModifyProxySettings(uid, packageName)) { Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings " + config.configKey() + ". Must have NETWORK_SETTINGS," + " or be device or profile owner."); @@ -1210,6 +1340,8 @@ public class WifiConfigManager { } if (mDeletedEphemeralSsidsToTimeMap.remove(config.SSID) != null) { + updateNetworkSelectionStatus( + newInternalConfig, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); if (mVerboseLoggingEnabled) { Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID); } @@ -1262,7 +1394,8 @@ public class WifiConfigManager { // In this case, new connection for this config won't happen because same // network is already registered as an ephemeral network. // Clear the Ephemeral Network to address the situation. - removeNetwork(existingConfig.networkId, mSystemUiUid); + removeNetwork( + existingConfig.networkId, existingConfig.creatorUid, config.creatorName); } } @@ -1280,16 +1413,17 @@ public class WifiConfigManager { // 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); - } + } + + for (OnNetworkUpdateListener listener : mListeners) { + WifiConfiguration configForListener = new WifiConfiguration(newConfig); + if (result.isNewNetwork()) { + listener.onNetworkAdded(configForListener); + } else { + listener.onNetworkUpdated(configForListener); } } return result; - } /** @@ -1343,7 +1477,7 @@ public class WifiConfigManager { * @param uid UID of the app requesting the network deletion. * @return true if successful, false otherwise. */ - public boolean removeNetwork(int networkId, int uid) { + public boolean removeNetwork(int networkId, int uid, String packageName) { if (!doesUidBelongToCurrentUser(uid)) { Log.e(TAG, "UID " + uid + " not visible to the current user"); return false; @@ -1352,7 +1486,7 @@ public class WifiConfigManager { if (config == null) { return false; } - if (!canModifyNetwork(config, uid)) { + if (!canModifyNetwork(config, uid, packageName)) { Log.e(TAG, "UID " + uid + " does not have permission to delete configuration " + config.configKey()); return false; @@ -1368,7 +1502,10 @@ public class WifiConfigManager { // 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); + } + for (OnNetworkUpdateListener listener : mListeners) { + WifiConfiguration configForListener = new WifiConfiguration(config); + listener.onNetworkRemoved(configForListener); } return true; } @@ -1406,8 +1543,8 @@ public class WifiConfigManager { } localLog("Removing network " + config.SSID + ", application \"" + app.packageName + "\" uninstalled" - + " from user " + UserHandle.getUserId(app.uid)); - if (removeNetwork(config.networkId, mSystemUiUid)) { + + " from user " + UserHandle.getUserHandleForUid(app.uid)); + if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) { removedNetworks.add(config.networkId); } } @@ -1427,11 +1564,11 @@ public class WifiConfigManager { WifiConfiguration[] copiedConfigs = mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); for (WifiConfiguration config : copiedConfigs) { - if (userId != UserHandle.getUserId(config.creatorUid)) { + if (userId != UserHandle.getUserHandleForUid(config.creatorUid).getIdentifier()) { continue; } localLog("Removing network " + config.SSID + ", user " + userId + " removed"); - if (removeNetwork(config.networkId, mSystemUiUid)) { + if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) { removedNetworks.add(config.networkId); } } @@ -1454,11 +1591,11 @@ public class WifiConfigManager { for (WifiConfiguration config : copiedConfigs) { if (config.isPasspoint()) { Log.d(TAG, "Removing passpoint network config " + config.configKey()); - removeNetwork(config.networkId, mSystemUiUid); + removeNetwork(config.networkId, config.creatorUid, config.creatorName); didRemove = true; } else if (config.ephemeral) { Log.d(TAG, "Removing ephemeral network config " + config.configKey()); - removeNetwork(config.networkId, mSystemUiUid); + removeNetwork(config.networkId, config.creatorUid, config.creatorName); didRemove = true; } } @@ -1466,20 +1603,31 @@ public class WifiConfigManager { } /** - * Removes the passpoint network configuration matched with {@code fqdn} provided. + * Removes the suggestion network configuration matched with {@code configKey} provided. * - * @param fqdn Fully Qualified Domain Name to remove. + * @param configKey Config Key for the corresponding network suggestion. * @return true if a network was removed, false otherwise. */ - public boolean removePasspointConfiguredNetwork(String fqdn) { - WifiConfiguration[] copiedConfigs = - mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); - for (WifiConfiguration config : copiedConfigs) { - if (config.isPasspoint() && TextUtils.equals(fqdn, config.FQDN)) { - Log.d(TAG, "Removing passpoint network config " + config.configKey()); - removeNetwork(config.networkId, mSystemUiUid); - return true; - } + public boolean removeSuggestionConfiguredNetwork(@NonNull String configKey) { + WifiConfiguration config = getInternalConfiguredNetwork(configKey); + if (config != null && config.ephemeral && config.fromWifiNetworkSuggestion) { + Log.d(TAG, "Removing suggestion network config " + config.configKey()); + return removeNetwork(config.networkId, config.creatorUid, config.creatorName); + } + return false; + } + + /** + * Removes the passpoint network configuration matched with {@code configKey} provided. + * + * @param configKey Config Key for the corresponding passpoint. + * @return true if a network was removed, false otherwise. + */ + public boolean removePasspointConfiguredNetwork(@NonNull String configKey) { + WifiConfiguration config = getInternalConfiguredNetwork(configKey); + if (config != null && config.isPasspoint()) { + Log.d(TAG, "Removing passpoint network config " + config.configKey()); + return removeNetwork(config.networkId, config.creatorUid, config.creatorName); } return false; } @@ -1497,7 +1645,10 @@ public class WifiConfigManager { // Clear out all the disable reason counters. status.clearDisableReasonCounter(); - if (mListener != null) mListener.onSavedNetworkEnabled(config.networkId); + for (OnNetworkUpdateListener listener : mListeners) { + WifiConfiguration configForListener = new WifiConfiguration(config); + listener.onNetworkEnabled(configForListener); + } } /** @@ -1511,8 +1662,9 @@ public class WifiConfigManager { // 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, disableReason); + for (OnNetworkUpdateListener listener : mListeners) { + WifiConfiguration configForListener = new WifiConfiguration(config); + listener.onNetworkTemporarilyDisabled(configForListener, disableReason); } } @@ -1527,8 +1679,9 @@ public class WifiConfigManager { status.setDisableTime( NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); status.setNetworkSelectionDisableReason(disableReason); - if (mListener != null) { - mListener.onSavedNetworkPermanentlyDisabled(config.networkId, disableReason); + for (OnNetworkUpdateListener listener : mListeners) { + WifiConfiguration configForListener = new WifiConfiguration(config); + listener.onNetworkPermanentlyDisabled(configForListener, disableReason); } } @@ -1724,7 +1877,8 @@ public class WifiConfigManager { * @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) { + public boolean enableNetwork(int networkId, boolean disableOthers, int uid, + String packageName) { if (mVerboseLoggingEnabled) { Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")"); } @@ -1742,7 +1896,7 @@ public class WifiConfigManager { if (disableOthers) { setLastSelectedNetwork(networkId); } - if (!canModifyNetwork(config, uid)) { + if (!canModifyNetwork(config, uid, packageName)) { Log.e(TAG, "UID " + uid + " does not have permission to update configuration " + config.configKey()); return false; @@ -1762,7 +1916,7 @@ public class WifiConfigManager { * @param uid uid of the app requesting the update. * @return true if it succeeds, false otherwise */ - public boolean disableNetwork(int networkId, int uid) { + public boolean disableNetwork(int networkId, int uid, String packageName) { if (mVerboseLoggingEnabled) { Log.v(TAG, "Disabling network " + networkId); } @@ -1779,7 +1933,7 @@ public class WifiConfigManager { if (networkId == mLastSelectedNetworkId) { clearLastSelectedNetwork(); } - if (!canModifyNetwork(config, uid)) { + if (!canModifyNetwork(config, uid, packageName)) { Log.e(TAG, "UID " + uid + " does not have permission to update configuration " + config.configKey()); return false; @@ -1793,6 +1947,30 @@ public class WifiConfigManager { } /** + * Changes the user's choice to allow auto-join using the + * {@link WifiManager#allowAutojoin(int, boolean)} API. + * + * @param networkId network ID of the network that needs the update. + * @param choice the choice to allow auto-join or not + * @return true if it succeeds, false otherwise + */ + public boolean allowAutojoin(int networkId, boolean choice) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Setting allowAutojoin to " + choice + " for netId " + networkId); + } + WifiConfiguration config = getInternalConfiguredNetwork(networkId); + if (config == null) { + Log.e(TAG, "allowAutojoin: Supplied networkId " + networkId + + " has no matching config"); + return false; + } + config.allowAutojoin = choice; + sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE); + saveToStore(true); + return true; + } + + /** * Updates the last connected UID for the provided configuration. * * @param networkId network ID corresponding to the network. @@ -1890,22 +2068,6 @@ public class WifiConfigManager { } /** - * Set randomized MAC address for the provided network. - * - * @param networkId network ID corresponding to the network. - * @param macAddress Randomized MAC address to be used for network connection. - * @return true if the network was found, false otherwise. - */ - public boolean setNetworkRandomizedMacAddress(int networkId, MacAddress macAddress) { - WifiConfiguration config = getInternalConfiguredNetwork(networkId); - if (config == null) { - return false; - } - config.setRandomizedMacAddress(macAddress); - return true; - } - - /** * Clear the {@link NetworkSelectionStatus#mCandidate}, * {@link NetworkSelectionStatus#mCandidateScore} & * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network. @@ -2708,6 +2870,16 @@ public class WifiConfigManager { return hiddenList; } + private @Nullable WifiConfiguration getInternalEphemeralConfiguredNetwork( + @NonNull String ssid) { + for (WifiConfiguration config : getInternalConfiguredNetworks()) { + if ((config.ephemeral || config.isPasspoint()) && TextUtils.equals(config.SSID, ssid)) { + return config; + } + } + return null; + } + /** * Check if the provided ephemeral network was deleted by the user or not. This call also clears * the SSID from the deleted ephemeral network map, if the duration has expired the @@ -2726,6 +2898,11 @@ public class WifiConfigManager { // Clear the ssid from the map if the age > |DELETED_EPHEMERAL_SSID_EXPIRY_MS|. if (nowInMs - deletedTimeInMs > DELETED_EPHEMERAL_SSID_EXPIRY_MS) { mDeletedEphemeralSsidsToTimeMap.remove(ssid); + WifiConfiguration foundConfig = getInternalEphemeralConfiguredNetwork(ssid); + if (foundConfig != null) { + updateNetworkSelectionStatus( + foundConfig, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); + } return false; } return true; @@ -2747,16 +2924,13 @@ public class WifiConfigManager { if (ssid == null) { return null; } - WifiConfiguration foundConfig = null; - for (WifiConfiguration config : getInternalConfiguredNetworks()) { - if ((config.ephemeral || config.isPasspoint()) && TextUtils.equals(config.SSID, ssid)) { - foundConfig = config; - break; - } - } + WifiConfiguration foundConfig = getInternalEphemeralConfiguredNetwork(ssid); if (foundConfig == null) return null; // Store the ssid & the wall clock time at which the network was disabled. mDeletedEphemeralSsidsToTimeMap.put(ssid, mClock.getWallClockMillis()); + // Also, mark the ephemeral permanently blacklisted. Will be taken out of blacklist + // when the ssid is taken out of |mDeletedEphemeralSsidsToTimeMap|. + updateNetworkSelectionStatus(foundConfig, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSsidsToTimeMap.size()); if (foundConfig.ephemeral) { @@ -2867,15 +3041,15 @@ public class WifiConfigManager { mPendingUnlockStoreRead = true; return new HashSet<>(); } - if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { + if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) { saveToStore(true); } // Remove any private networks of the old user before switching the userId. - Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId); + Set<Integer> removedNetworkIds = clearInternalDataForCurrentUser(); mConfiguredNetworks.setNewUser(userId); mCurrentUserId = userId; - if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { + if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) { handleUserUnlockOrSwitch(mCurrentUserId); } else { // Cannot read data from new user's CE store file before they log-in. @@ -2923,9 +3097,10 @@ public class WifiConfigManager { if (mVerboseLoggingEnabled) { Log.v(TAG, "Handling user stop for " + userId); } - if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { + if (userId == mCurrentUserId + && mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) { saveToStore(true); - clearInternalUserData(mCurrentUserId); + clearInternalDataForCurrentUser(); } } @@ -2952,17 +3127,15 @@ public class WifiConfigManager { * - 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) { - localLog("clearInternalUserData: Clearing user internal data for " + userId); + private Set<Integer> clearInternalDataForCurrentUser() { + localLog("clearInternalUserData: Clearing user internal data for " + mCurrentUserId); 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))) { + if (!config.shared && doesUidBelongToCurrentUser(config.creatorUid)) { removedNetworkIds.add(config.networkId); localLog("clearInternalUserData: removed config." + " netId=" + config.networkId @@ -3026,12 +3199,16 @@ public class WifiConfigManager { } /** - * Generate randomized MAC addresses for configured networks and persist mapping to storage. + * Assign randomized MAC addresses for configured networks. + * This is needed to generate persistent randomized MAC address for existing networks when + * a device updates to Q+ for the first time since we are not calling addOrUpdateNetwork when + * we load configuration at boot. */ private void generateRandomizedMacAddresses() { for (WifiConfiguration config : getInternalConfiguredNetworks()) { - mRandomizedMacAddressMapping.put(config.getSsidAndSecurityTypeString(), - config.getOrCreateRandomizedMacAddress().toString()); + if (DEFAULT_MAC_ADDRESS.equals(config.getRandomizedMacAddress())) { + setRandomizedMacToPersistentMac(config); + } } } @@ -3168,8 +3345,7 @@ public class WifiConfigManager { // Migrate the legacy Passpoint configurations owned by the current user to // {@link PasspointManager}. - if (config.isLegacyPasspointConfig && WifiConfigurationUtil.doesUidBelongToAnyProfile( - config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) { + if (config.isLegacyPasspointConfig && doesUidBelongToCurrentUser(config.creatorUid)) { legacyPasspointNetId.add(config.networkId); // Migrate the legacy Passpoint configuration and add it to PasspointManager. if (!PasspointManager.addLegacyPasspointConfig(config)) { @@ -3186,8 +3362,7 @@ public class WifiConfigManager { // 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))) { + if (config.shared || !doesUidBelongToCurrentUser(config.creatorUid)) { sharedConfigurations.add(config); } else { userConfigurations.add(config); @@ -3251,19 +3426,15 @@ public class WifiConfigManager { /** * 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); + private boolean canModifyProxySettings(int uid, String packageName) { + final boolean isDeviceOwner = mWifiPermissionsUtil.isDeviceOwner(uid, packageName); + final boolean isProfileOwner = mWifiPermissionsUtil.isProfileOwner(uid, packageName); final boolean hasNetworkSettingsPermission = mWifiPermissionsUtil.checkNetworkSettingsPermission(uid); final boolean hasNetworkSetupWizardPermission = mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid); // If |uid| corresponds to the device owner, allow all modifications. - if (isUidDeviceOwner || isUidProfileOwner || hasNetworkSettingsPermission + if (isProfileOwner || isDeviceOwner || hasNetworkSettingsPermission || hasNetworkSetupWizardPermission) { return true; } @@ -3271,17 +3442,17 @@ public class WifiConfigManager { Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings." + " hasNetworkSettings=" + hasNetworkSettingsPermission + " hasNetworkSetupWizard=" + hasNetworkSetupWizardPermission - + " DeviceOwner=" + isUidDeviceOwner - + " ProfileOwner=" + isUidProfileOwner); + + " DeviceOwner=" + isDeviceOwner + + " ProfileOwner=" + isProfileOwner); } return false; } /** - * Set the saved network update event listener + * Set the network update event listener */ - public void setOnSavedNetworkUpdateListener(OnSavedNetworkUpdateListener listener) { - mListener = listener; + public void addOnNetworkUpdateListener(OnNetworkUpdateListener listener) { + mListeners.add(listener); } /** diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java index e189d00e1d..083f442ca9 100644 --- a/service/java/com/android/server/wifi/WifiConfigStore.java +++ b/service/java/com/android/server/wifi/WifiConfigStore.java @@ -23,20 +23,19 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.content.Context; -import android.os.Environment; -import android.os.FileUtils; import android.os.Handler; -import android.os.Looper; +import android.util.AtomicFile; import android.util.Log; import android.util.SparseArray; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.server.wifi.util.DataIntegrityChecker; import com.android.server.wifi.util.EncryptedData; +import com.android.server.wifi.util.Environment; +import com.android.server.wifi.util.FileUtils; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -218,17 +217,17 @@ public class 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 handler handler instance to post alarm timeouts to. * @param clock clock instance to retrieve timestamps for alarms. * @param wifiMetrics Metrics instance. * @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, WifiMetrics wifiMetrics, + public WifiConfigStore(Context context, Handler handler, Clock clock, WifiMetrics wifiMetrics, StoreFile sharedStore) { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - mEventHandler = new Handler(looper); + mEventHandler = handler; mClock = clock; mWifiMetrics = wifiMetrics; mStoreDataList = new ArrayList<>(); @@ -685,15 +684,16 @@ public class WifiConfigStore { String[] headerName = new String[1]; Set<StoreData> storeDatasInvoked = new HashSet<>(); while (XmlUtil.gotoNextSectionOrEnd(in, headerName, rootTagDepth)) { - // There can only be 1 store data matching the tag (O indicates a fatal - // error). + // There can only be 1 store data matching the tag, O indicates a previous StoreData + // module that no longer exists (ignore this XML section). StoreData storeData = storeDataList.stream() .filter(s -> s.getName().equals(headerName[0])) .findAny() .orElse(null); if (storeData == null) { - throw new XmlPullParserException("Unknown store data: " + headerName[0] - + ". List of store data: " + storeDataList); + Log.e(TAG, "Unknown store data: " + headerName[0] + ". List of store data: " + + storeDataList); + continue; } storeData.deserializeData(in, rootTagDepth + 1); storeDatasInvoked.add(storeData); diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java index 59d3eb3f49..f79cc9230e 100644 --- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java +++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java @@ -18,7 +18,6 @@ package com.android.server.wifi; import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; -import android.content.pm.UserInfo; import android.net.IpConfiguration; import android.net.MacAddress; import android.net.StaticIpConfiguration; @@ -27,7 +26,9 @@ import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiNetworkSpecifier; import android.net.wifi.WifiScanner; import android.os.PatternMatcher; -import android.os.UserHandle; +import android.security.keystore.AndroidKeyStoreProvider; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -36,14 +37,27 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.TelephonyUtil; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.ProviderException; +import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.BitSet; import java.util.Comparator; -import java.util.List; import java.util.Objects; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKey; + /** * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations. * Currently contains: @@ -65,44 +79,19 @@ public class WifiConfigurationUtil { private static final int SAE_ASCII_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; private static final int PSK_SAE_ASCII_MAX_LEN = 63 + ENCLOSING_QUOTES_LEN; private static final int PSK_SAE_HEX_LEN = 64; + private static final MacAddress ALL_ZEROS_MAC_ADDRESS = + MacAddress.fromString("00:00:00:00:00:00"); @VisibleForTesting public static final String PASSWORD_MASK = "*"; private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; private static final Pair<MacAddress, MacAddress> MATCH_NONE_BSSID_PATTERN = new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN = - new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); - - /** - * Check whether a network configuration is visible to a user or any of its managed profiles. - * - * @param config the network configuration whose visibility should be checked - * @param profiles the user IDs of the user itself and all its managed profiles (can be obtained - * via {@link android.os.UserManager#getProfiles}) - * @return whether the network configuration is visible to the user or any of its managed - * profiles - */ - public static boolean isVisibleToAnyProfile(WifiConfiguration config, List<UserInfo> profiles) { - 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 == userId) { - return true; - } - } - return false; - } + new Pair(ALL_ZEROS_MAC_ADDRESS, ALL_ZEROS_MAC_ADDRESS); + private static final String MAC_RANDOMIZATION_ALIAS = "MacRandSecret"; + private static final long MAC_ADDRESS_VALID_LONG_MASK = (1L << 48) - 1; + private static final long MAC_ADDRESS_LOCALLY_ASSIGNED_MASK = 1L << 41; + private static final long MAC_ADDRESS_MULTICAST_MASK = 1L << 40; /** * Checks if the provided |wepKeys| array contains any non-null value; @@ -227,6 +216,87 @@ public class WifiConfigurationUtil { } /** + * Computes the persistent randomized MAC of the given configuration using the given + * hash function. + * @param config the WifiConfiguration to compute MAC address for + * @param hashFunction the hash function that will perform the MAC address computation. + * @return The persistent randomized MAC address or null if inputs are invalid. + */ + public static MacAddress calculatePersistentMacForConfiguration(WifiConfiguration config, + Mac hashFunction) { + if (config == null || hashFunction == null) { + return null; + } + byte[] hashedBytes = hashFunction.doFinal( + config.getSsidAndSecurityTypeString().getBytes(StandardCharsets.UTF_8)); + ByteBuffer bf = ByteBuffer.wrap(hashedBytes); + long longFromSsid = bf.getLong(); + /** + * Masks the generated long so that it represents a valid randomized MAC address. + * Specifically, this sets the locally assigned bit to 1, multicast bit to 0 + */ + longFromSsid &= MAC_ADDRESS_VALID_LONG_MASK; + longFromSsid |= MAC_ADDRESS_LOCALLY_ASSIGNED_MASK; + longFromSsid &= ~MAC_ADDRESS_MULTICAST_MASK; + bf.clear(); + bf.putLong(0, longFromSsid); + + // MacAddress.fromBytes requires input of length 6, which is obtained from the + // last 6 bytes from the generated long. + MacAddress macAddress = MacAddress.fromBytes(Arrays.copyOfRange(bf.array(), 2, 8)); + return macAddress; + } + + /** + * Retrieves a Hash function that could be used to calculate the persistent randomized MAC + * for a WifiConfiguration. + * @param uid the UID of the KeyStore to get the secret of the hash function from. + */ + public static Mac obtainMacRandHashFunction(int uid) { + try { + KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(uid); + // tries to retrieve the secret, and generate a new one if it's unavailable. + Key key = keyStore.getKey(MAC_RANDOMIZATION_ALIAS, null); + if (key == null) { + key = generateAndPersistNewMacRandomizationSecret(uid); + } + if (key == null) { + Log.e(TAG, "Failed to generate secret for " + MAC_RANDOMIZATION_ALIAS); + return null; + } + Mac result = Mac.getInstance("HmacSHA256"); + result.init(key); + return result; + } catch (KeyStoreException | NoSuchAlgorithmException | InvalidKeyException + | UnrecoverableKeyException | NoSuchProviderException e) { + Log.e(TAG, "Failure in obtainMacRandHashFunction", e); + return null; + } + } + + /** + * Generates and returns a secret key to use for Mac randomization. + * Will also persist the generated secret inside KeyStore, accessible in the + * future with KeyGenerator#getKey. + */ + private static SecretKey generateAndPersistNewMacRandomizationSecret(int uid) { + try { + KeyGenerator keyGenerator = KeyGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore"); + keyGenerator.init( + new KeyGenParameterSpec.Builder(MAC_RANDOMIZATION_ALIAS, + KeyProperties.PURPOSE_SIGN) + .setUid(uid) + .build()); + return keyGenerator.generateKey(); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException + | NoSuchProviderException | ProviderException e) { + Log.e(TAG, "Failure in generateMacRandomizationSecret", e); + return null; + } + } + + /** * Compare existing and new WifiEnterpriseConfig objects after a network update and return if * credential parameters have changed or not. * @@ -639,8 +709,8 @@ public class WifiConfigurationUtil { Log.e(TAG, "validateBssidPatternMatcher failed : invalid base address: " + baseAddress); return false; } - if (mask.equals(MacAddress.ALL_ZEROS_ADDRESS) - && !baseAddress.equals(MacAddress.ALL_ZEROS_ADDRESS)) { + if (mask.equals(ALL_ZEROS_MAC_ADDRESS) + && !baseAddress.equals(ALL_ZEROS_MAC_ADDRESS)) { Log.e(TAG, "validateBssidPatternMatcher failed : invalid mask/base: " + mask + "/" + baseAddress); return false; diff --git a/service/java/com/android/server/wifi/WifiConnectivityHelper.java b/service/java/com/android/server/wifi/WifiConnectivityHelper.java index ed541a9591..248877f3bb 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityHelper.java +++ b/service/java/com/android/server/wifi/WifiConnectivityHelper.java @@ -29,7 +29,7 @@ import java.util.ArrayList; * 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 ClientModeImpl thread. + * NOTE: This class is not thread safe and should only be used from the main Wifi thread. */ public class WifiConnectivityHelper { private static final String TAG = "WifiConnectivityHelper"; @@ -162,14 +162,4 @@ public class WifiConnectivityHelper { return mWifiNative.configureRoaming(mWifiNative.getClientInterfaceName(), 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(mWifiNative.getClientInterfaceName(), networkId); - } } diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index 2e4b5c891a..b1d0a05fe9 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -16,9 +16,6 @@ package com.android.server.wifi; -import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT; -import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; - import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; @@ -34,7 +31,6 @@ import android.net.wifi.WifiScanner; import android.net.wifi.WifiScanner.PnoSettings; import android.net.wifi.WifiScanner.ScanSettings; import android.os.Handler; -import android.os.Looper; import android.os.Process; import android.os.WorkSource; import android.util.LocalLog; @@ -143,7 +139,6 @@ public class WifiConnectivityManager { private final WifiNetworkSelector mNetworkSelector; private final WifiLastResortWatchdog mWifiLastResortWatchdog; private final OpenNetworkNotifier mOpenNetworkNotifier; - private final CarrierNetworkNotifier mCarrierNetworkNotifier; private final CarrierNetworkConfig mCarrierNetworkConfig; private final WifiMetrics mWifiMetrics; private final AlarmManager mAlarmManager; @@ -289,11 +284,6 @@ public class WifiConnectivityManager { if (mWifiState == WIFI_STATE_DISCONNECTED) { mOpenNetworkNotifier.handleScanResults( mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks()); - if (mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()) { - mCarrierNetworkNotifier.handleScanResults( - mNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig)); - } } return false; } @@ -539,39 +529,29 @@ public class WifiConnectivityManager { private final PnoScanListener mPnoScanListener = new PnoScanListener(); - private class OnSavedNetworkUpdateListener implements - WifiConfigManager.OnSavedNetworkUpdateListener { + private class OnNetworkUpdateListener implements + WifiConfigManager.OnNetworkUpdateListener { @Override - public void onSavedNetworkAdded(int networkId) { + public void onNetworkAdded(WifiConfiguration config) { updatePnoScan(); } @Override - public void onSavedNetworkEnabled(int networkId) { + public void onNetworkEnabled(WifiConfiguration config) { updatePnoScan(); } @Override - public void onSavedNetworkRemoved(int networkId) { + public void onNetworkRemoved(WifiConfiguration config) { updatePnoScan(); } @Override - public void onSavedNetworkUpdated(int networkId) { - // User might have changed meteredOverride, so update capabilties - mStateMachine.updateCapabilities(); + public void onNetworkUpdated(WifiConfiguration config) { updatePnoScan(); } @Override - public void onSavedNetworkTemporarilyDisabled(int networkId, int disableReason) { - if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return; - mConnectivityHelper.removeNetworkIfCurrent(networkId); - } + public void onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason) { } + @Override - public void onSavedNetworkPermanentlyDisabled(int networkId, int disableReason) { - // For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network - // because supplicant won't be trying to reconnect. If this is due to a - // preventAutomaticReconnect request from ConnectivityService, that service - // will disconnect as appropriate. - if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return; - mConnectivityHelper.removeNetworkIfCurrent(networkId); + public void onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason) { updatePnoScan(); } private void updatePnoScan() { @@ -592,8 +572,7 @@ public class WifiConnectivityManager { WifiInjector injector, WifiConfigManager configManager, WifiInfo wifiInfo, WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier, - CarrierNetworkNotifier carrierNetworkNotifier, - CarrierNetworkConfig carrierNetworkConfig, WifiMetrics wifiMetrics, Looper looper, + CarrierNetworkConfig carrierNetworkConfig, WifiMetrics wifiMetrics, Handler handler, Clock clock, LocalLog localLog) { mStateMachine = stateMachine; mWifiInjector = injector; @@ -604,11 +583,10 @@ public class WifiConnectivityManager { mLocalLog = localLog; mWifiLastResortWatchdog = wifiLastResortWatchdog; mOpenNetworkNotifier = openNetworkNotifier; - mCarrierNetworkNotifier = carrierNetworkNotifier; mCarrierNetworkConfig = carrierNetworkConfig; mWifiMetrics = wifiMetrics; mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - mEventHandler = new Handler(looper); + mEventHandler = handler; mClock = clock; mScoringParams = scoringParams; mConnectionAttemptTimeStamps = new LinkedList<>(); @@ -647,7 +625,7 @@ public class WifiConnectivityManager { + " initialScoreMax " + initialScoreMax()); // Listen to WifiConfigManager network update events - mConfigManager.setOnSavedNetworkUpdateListener(new OnSavedNetworkUpdateListener()); + mConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener()); } /** Returns maximum PNO score, before any awards/bonuses. */ @@ -1146,7 +1124,6 @@ public class WifiConnectivityManager { mScreenOn = screenOn; mOpenNetworkNotifier.handleScreenStateChanged(screenOn); - mCarrierNetworkNotifier.handleScreenStateChanged(screenOn); startConnectivityScan(SCAN_ON_SCHEDULE); } @@ -1197,10 +1174,8 @@ public class WifiConnectivityManager { ? null : mWifiInfo.getWifiSsid().toString(); mOpenNetworkNotifier.handleWifiConnected(ssid); - mCarrierNetworkNotifier.handleWifiConnected(ssid); } else { mOpenNetworkNotifier.handleConnectionFailure(); - mCarrierNetworkNotifier.handleConnectionFailure(); } } @@ -1390,9 +1365,13 @@ public class WifiConnectivityManager { } int maxBlacklistSize = mConnectivityHelper.getMaxNumBlacklistBssid(); - if (maxBlacklistSize <= 0) { + if (maxBlacklistSize < 0) { Log.wtf(TAG, "Invalid max BSSID blacklist size: " + maxBlacklistSize); return; + } else if (maxBlacklistSize == 0) { + Log.d(TAG, "Skip setting firmware roaming configuration" + + " since max BSSID blacklist size is zero"); + return; } ArrayList<String> blacklistedBssids = new ArrayList<String>(buildBssidBlacklist()); @@ -1485,7 +1464,6 @@ public class WifiConnectivityManager { clearBssidBlacklist(); resetLastPeriodicSingleScanTimeStamp(); mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */); - mCarrierNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */); mLastConnectionAttemptBssid = null; mWaitForFullBandScanResults = false; } @@ -1545,7 +1523,6 @@ public class WifiConnectivityManager { mLocalLog.dump(fd, pw, args); pw.println("WifiConnectivityManager - Log End ----"); mOpenNetworkNotifier.dump(fd, pw, args); - mCarrierNetworkNotifier.dump(fd, pw, args); mCarrierNetworkConfig.dump(fd, pw, args); } } diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java deleted file mode 100644 index bfa2ae0331..0000000000 --- a/service/java/com/android/server/wifi/WifiController.java +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wifi; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.location.LocationManager; -import android.net.wifi.WifiManager; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.provider.Settings; -import android.util.Log; - -import com.android.internal.R; -import com.android.internal.util.Protocol; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.server.wifi.util.WifiPermissionsUtil; - -/** - * WifiController is the class used to manage wifi state for various operating - * modes (normal, airplane, wifi hotspot, etc.). - */ -public class WifiController extends StateMachine { - private static final String TAG = "WifiController"; - private static final boolean DBG = false; - private Context mContext; - private boolean mFirstUserSignOnSeen = false; - - /** - * See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}. This is the default value if a - * Settings.Global value is not present. This is the minimum time after wifi is disabled - * we'll act on an enable. Enable requests received before this delay will be deferred. - */ - private static final long DEFAULT_REENABLE_DELAY_MS = 500; - - // Maximum limit to use for timeout delay if the value from overlay setting is too large. - private static final int MAX_RECOVERY_TIMEOUT_DELAY_MS = 4000; - - // finding that delayed messages can sometimes be delivered earlier than expected - // probably rounding errors. add a margin to prevent problems - private static final long DEFER_MARGIN_MS = 5; - - /* References to values tracked in WifiService */ - private final ClientModeImpl mClientModeImpl; - private final Looper mClientModeImplLooper; - private final ActiveModeWarden mActiveModeWarden; - private final WifiSettingsStore mSettingsStore; - private final FrameworkFacade mFacade; - private final WifiPermissionsUtil mWifiPermissionsUtil; - - private long mReEnableDelayMillis; - - private int mRecoveryDelayMillis; - - private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; - - static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; - static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; - static final int CMD_WIFI_TOGGLED = BASE + 8; - static final int CMD_AIRPLANE_TOGGLED = BASE + 9; - static final int CMD_SET_AP = BASE + 10; - static final int CMD_DEFERRED_TOGGLE = BASE + 11; - static final int CMD_AP_START_FAILURE = BASE + 13; - static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; - static final int CMD_AP_STOPPED = BASE + 15; - static final int CMD_STA_START_FAILURE = BASE + 16; - // Command used to trigger a wifi stack restart when in active mode - static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; - // Internal command used to complete wifi stack restart - private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; - // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full recovery - static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; - static final int CMD_STA_STOPPED = BASE + 20; - static final int CMD_SCANNING_STOPPED = BASE + 21; - static final int CMD_DEFERRED_RECOVERY_RESTART_WIFI = BASE + 22; - - private DefaultState mDefaultState = new DefaultState(); - private StaEnabledState mStaEnabledState = new StaEnabledState(); - private StaDisabledState mStaDisabledState = new StaDisabledState(); - private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState(); - private EcmState mEcmState = new EcmState(); - - private ScanOnlyModeManager.Listener mScanOnlyModeCallback = new ScanOnlyCallback(); - private ClientModeManager.Listener mClientModeCallback = new ClientModeCallback(); - - WifiController(Context context, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, - WifiSettingsStore wss, Looper wifiServiceLooper, FrameworkFacade f, - ActiveModeWarden amw, WifiPermissionsUtil wifiPermissionsUtil) { - super(TAG, wifiServiceLooper); - mFacade = f; - mContext = context; - mClientModeImpl = clientModeImpl; - mClientModeImplLooper = clientModeImplLooper; - mActiveModeWarden = amw; - mSettingsStore = wss; - mWifiPermissionsUtil = wifiPermissionsUtil; - - // CHECKSTYLE:OFF IndentationCheck - addState(mDefaultState); - addState(mStaDisabledState, mDefaultState); - addState(mStaEnabledState, mDefaultState); - addState(mStaDisabledWithScanState, mDefaultState); - addState(mEcmState, mDefaultState); - // CHECKSTYLE:ON IndentationCheck - - setLogRecSize(100); - setLogOnlyTransitions(false); - - // register for state updates via callbacks (vs the intents registered below) - mActiveModeWarden.registerScanOnlyCallback(mScanOnlyModeCallback); - mActiveModeWarden.registerClientModeCallback(mClientModeCallback); - - readWifiReEnableDelay(); - readWifiRecoveryDelay(); - } - - @Override - public void start() { - boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); - boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); - boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); - boolean isLocationModeActive = mWifiPermissionsUtil.isLocationModeEnabled(); - - log("isAirplaneModeOn = " + isAirplaneModeOn - + ", isWifiEnabled = " + isWifiEnabled - + ", isScanningAvailable = " + isScanningAlwaysAvailable - + ", isLocationModeActive = " + isLocationModeActive); - - if (checkScanOnlyModeAvailable()) { - setInitialState(mStaDisabledWithScanState); - } else { - setInitialState(mStaDisabledState); - } - IntentFilter filter = new IntentFilter(); - filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - filter.addAction(LocationManager.MODE_CHANGED_ACTION); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { - int state = intent.getIntExtra( - WifiManager.EXTRA_WIFI_AP_STATE, - WifiManager.WIFI_AP_STATE_FAILED); - if (state == WifiManager.WIFI_AP_STATE_FAILED) { - Log.e(TAG, "SoftAP start failed"); - sendMessage(CMD_AP_START_FAILURE); - } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) { - sendMessage(CMD_AP_STOPPED); - } - } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) { - // Location mode has been toggled... trigger with the scan change - // update to make sure we are in the correct mode - sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - } - } - }, - new IntentFilter(filter)); - super.start(); - } - - private boolean checkScanOnlyModeAvailable() { - // first check if Location service is disabled, if so return false - if (!mWifiPermissionsUtil.isLocationModeEnabled()) { - return false; - } - return mSettingsStore.isScanAlwaysAvailable(); - } - - /** - * Listener used to receive scan mode updates - really needed for disabled updates to trigger - * mode changes. - */ - private class ScanOnlyCallback implements ScanOnlyModeManager.Listener { - @Override - public void onStateChanged(int state) { - if (state == WifiManager.WIFI_STATE_UNKNOWN) { - Log.d(TAG, "ScanOnlyMode unexpected failure: state unknown"); - } else if (state == WifiManager.WIFI_STATE_DISABLED) { - Log.d(TAG, "ScanOnlyMode stopped"); - sendMessage(CMD_SCANNING_STOPPED); - } else if (state == WifiManager.WIFI_STATE_ENABLED) { - // scan mode is ready to go - Log.d(TAG, "scan mode active"); - } else { - Log.d(TAG, "unexpected state update: " + state); - } - } - } - - /** - * Listener used to receive client mode updates - */ - private class ClientModeCallback implements ClientModeManager.Listener { - @Override - public void onStateChanged(int state) { - if (state == WifiManager.WIFI_STATE_UNKNOWN) { - logd("ClientMode unexpected failure: state unknown"); - sendMessage(CMD_STA_START_FAILURE); - } else if (state == WifiManager.WIFI_STATE_DISABLED) { - logd("ClientMode stopped"); - sendMessage(CMD_STA_STOPPED); - } else if (state == WifiManager.WIFI_STATE_ENABLED) { - // scan mode is ready to go - logd("client mode active"); - } else { - logd("unexpected state update: " + state); - } - } - } - - private void readWifiReEnableDelay() { - mReEnableDelayMillis = mFacade.getLongSetting(mContext, - Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS); - } - - private void readWifiRecoveryDelay() { - mRecoveryDelayMillis = mContext.getResources().getInteger( - R.integer.config_wifi_framework_recovery_timeout_delay); - if (mRecoveryDelayMillis > MAX_RECOVERY_TIMEOUT_DELAY_MS) { - mRecoveryDelayMillis = MAX_RECOVERY_TIMEOUT_DELAY_MS; - Log.w(TAG, "Overriding timeout delay with maximum limit value"); - } - } - - class DefaultState extends State { - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_SCAN_ALWAYS_MODE_CHANGED: - case CMD_WIFI_TOGGLED: - case CMD_AP_START_FAILURE: - case CMD_SCANNING_STOPPED: - case CMD_STA_STOPPED: - case CMD_STA_START_FAILURE: - case CMD_RECOVERY_RESTART_WIFI_CONTINUE: - case CMD_DEFERRED_RECOVERY_RESTART_WIFI: - break; - case CMD_RECOVERY_DISABLE_WIFI: - log("Recovery has been throttled, disable wifi"); - mActiveModeWarden.shutdownWifi(); - transitionTo(mStaDisabledState); - break; - case CMD_RECOVERY_RESTART_WIFI: - deferMessage(obtainMessage(CMD_DEFERRED_RECOVERY_RESTART_WIFI)); - mActiveModeWarden.shutdownWifi(); - transitionTo(mStaDisabledState); - break; - case CMD_DEFERRED_TOGGLE: - log("DEFERRED_TOGGLE ignored due to state change"); - break; - case CMD_SET_AP: - // note: CMD_SET_AP is handled/dropped in ECM mode - will not start here - if (msg.arg1 == 1) { - SoftApModeConfiguration config = (SoftApModeConfiguration) msg.obj; - mActiveModeWarden.enterSoftAPMode((SoftApModeConfiguration) msg.obj); - } else { - mActiveModeWarden.stopSoftAPMode(msg.arg2); - } - break; - case CMD_AIRPLANE_TOGGLED: - if (mSettingsStore.isAirplaneModeOn()) { - log("Airplane mode toggled, shutdown all modes"); - mActiveModeWarden.shutdownWifi(); - transitionTo(mStaDisabledState); - } else { - log("Airplane mode disabled, determine next state"); - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } - // wifi should remain disabled, do not need to transition - } - break; - case CMD_EMERGENCY_CALL_STATE_CHANGED: - case CMD_EMERGENCY_MODE_CHANGED: - if (msg.arg1 == 1) { - transitionTo(mEcmState); - } - break; - case CMD_AP_STOPPED: - log("SoftAp mode disabled, determine next state"); - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } - // wifi should remain disabled, do not need to transition - break; - default: - throw new RuntimeException("WifiController.handleMessage " + msg.what); - } - return HANDLED; - } - - } - - class StaDisabledState extends State { - private int mDeferredEnableSerialNumber = 0; - private boolean mHaveDeferredEnable = false; - private long mDisabledTimestamp; - - @Override - public void enter() { - mActiveModeWarden.disableWifi(); - // Supplicant can't restart right away, so note the time we switched off - mDisabledTimestamp = SystemClock.elapsedRealtime(); - mDeferredEnableSerialNumber++; - mHaveDeferredEnable = false; - } - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_WIFI_TOGGLED: - if (mSettingsStore.isWifiToggleEnabled()) { - if (doDeferEnable(msg)) { - if (mHaveDeferredEnable) { - // have 2 toggles now, inc serial number and ignore both - mDeferredEnableSerialNumber++; - } - mHaveDeferredEnable = !mHaveDeferredEnable; - break; - } - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - // only go to scan mode if we aren't in airplane mode - if (mSettingsStore.isAirplaneModeOn()) { - transitionTo(mStaDisabledWithScanState); - } - } - break; - case CMD_SCAN_ALWAYS_MODE_CHANGED: - if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - break; - } - break; - case CMD_SET_AP: - if (msg.arg1 == 1) { - // remember that we were disabled, but pass the command up to start softap - mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); - } - return NOT_HANDLED; - case CMD_DEFERRED_TOGGLE: - if (msg.arg1 != mDeferredEnableSerialNumber) { - log("DEFERRED_TOGGLE ignored due to serial mismatch"); - break; - } - log("DEFERRED_TOGGLE handled"); - sendMessage((Message)(msg.obj)); - break; - case CMD_DEFERRED_RECOVERY_RESTART_WIFI: - // wait mRecoveryDelayMillis for letting driver clean reset. - sendMessageDelayed(CMD_RECOVERY_RESTART_WIFI_CONTINUE, mRecoveryDelayMillis); - break; - case CMD_RECOVERY_RESTART_WIFI_CONTINUE: - if (mSettingsStore.isWifiToggleEnabled()) { - // wifi is currently disabled but the toggle is on, must have had an - // interface down before the recovery triggered - transitionTo(mStaEnabledState); - break; - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - break; - } - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - - private boolean doDeferEnable(Message msg) { - long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; - if (delaySoFar >= mReEnableDelayMillis) { - return false; - } - - log("WifiController msg " + msg + " deferred for " + - (mReEnableDelayMillis - delaySoFar) + "ms"); - - // need to defer this action. - Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); - deferredMsg.obj = Message.obtain(msg); - deferredMsg.arg1 = ++mDeferredEnableSerialNumber; - sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); - return true; - } - - } - - class StaEnabledState extends State { - @Override - public void enter() { - log("StaEnabledState.enter()"); - mActiveModeWarden.enterClientMode(); - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_WIFI_TOGGLED: - if (! mSettingsStore.isWifiToggleEnabled()) { - if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } else { - transitionTo(mStaDisabledState); - } - } - break; - case CMD_AIRPLANE_TOGGLED: - // airplane mode toggled on is handled in the default state - if (mSettingsStore.isAirplaneModeOn()) { - return NOT_HANDLED; - } else { - // when airplane mode is toggled off, but wifi is on, we can keep it on - log("airplane mode toggled - and airplane mode is off. return handled"); - return HANDLED; - } - case CMD_STA_START_FAILURE: - if (!checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledState); - } else { - transitionTo(mStaDisabledWithScanState); - } - break; - case CMD_SET_AP: - if (msg.arg1 == 1) { - // remember that we were enabled, but pass the command up to start softap - mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED); - } - return NOT_HANDLED; - case CMD_AP_START_FAILURE: - case CMD_AP_STOPPED: - // already in a wifi mode, no need to check where we should go with softap - // stopped - break; - case CMD_STA_STOPPED: - // Client mode stopped. head to Disabled to wait for next command - transitionTo(mStaDisabledState); - break; - case CMD_RECOVERY_RESTART_WIFI: - final String bugTitle; - final String bugDetail; - if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) { - bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1]; - bugTitle = "Wi-Fi BugReport: " + bugDetail; - } else { - bugDetail = ""; - bugTitle = "Wi-Fi BugReport"; - } - if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) { - (new Handler(mClientModeImplLooper)).post(() -> { - mClientModeImpl.takeBugReport(bugTitle, bugDetail); - }); - } - // after the bug report trigger, more handling needs to be done - return NOT_HANDLED; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class StaDisabledWithScanState extends State { - private int mDeferredEnableSerialNumber = 0; - private boolean mHaveDeferredEnable = false; - private long mDisabledTimestamp; - - @Override - public void enter() { - // now trigger the actual mode switch in ActiveModeWarden - mActiveModeWarden.enterScanOnlyMode(); - - // TODO b/71559473: remove the defered enable after mode management changes are complete - // Supplicant can't restart right away, so not the time we switched off - mDisabledTimestamp = SystemClock.elapsedRealtime(); - mDeferredEnableSerialNumber++; - mHaveDeferredEnable = false; - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_WIFI_TOGGLED: - if (mSettingsStore.isWifiToggleEnabled()) { - if (doDeferEnable(msg)) { - if (mHaveDeferredEnable) { - // have 2 toggles now, inc serial number and ignore both - mDeferredEnableSerialNumber++; - } - mHaveDeferredEnable = !mHaveDeferredEnable; - break; - } - transitionTo(mStaEnabledState); - } - break; - case CMD_SCAN_ALWAYS_MODE_CHANGED: - if (!checkScanOnlyModeAvailable()) { - log("StaDisabledWithScanState: scan no longer available"); - transitionTo(mStaDisabledState); - } - break; - case CMD_SET_AP: - if (msg.arg1 == 1) { - // remember that we were disabled, but pass the command up to start softap - mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); - } - return NOT_HANDLED; - case CMD_DEFERRED_TOGGLE: - if (msg.arg1 != mDeferredEnableSerialNumber) { - log("DEFERRED_TOGGLE ignored due to serial mismatch"); - break; - } - logd("DEFERRED_TOGGLE handled"); - sendMessage((Message)(msg.obj)); - break; - case CMD_AP_START_FAILURE: - case CMD_AP_STOPPED: - // already in a wifi mode, no need to check where we should go with softap - // stopped - break; - case CMD_SCANNING_STOPPED: - // stopped due to interface destruction - return to disabled and wait - log("WifiController: SCANNING_STOPPED when in scan mode -> StaDisabled"); - transitionTo(mStaDisabledState); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - - private boolean doDeferEnable(Message msg) { - long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; - if (delaySoFar >= mReEnableDelayMillis) { - return false; - } - - log("WifiController msg " + msg + " deferred for " + - (mReEnableDelayMillis - delaySoFar) + "ms"); - - // need to defer this action. - Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); - deferredMsg.obj = Message.obtain(msg); - deferredMsg.arg1 = ++mDeferredEnableSerialNumber; - sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); - return true; - } - - } - - /** - * Determine the next state based on the current settings (e.g. saved - * wifi state). - */ - private State getNextWifiState() { - if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) { - return mStaEnabledState; - } - - if (checkScanOnlyModeAvailable()) { - return mStaDisabledWithScanState; - } - - return mStaDisabledState; - } - - class EcmState extends State { - // we can enter EcmState either because an emergency call started or because - // emergency callback mode started. This count keeps track of how many such - // events happened; so we can exit after all are undone - - private int mEcmEntryCount; - @Override - public void enter() { - mActiveModeWarden.stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); - boolean configWiFiDisableInECBM = - mFacade.getConfigWiFiDisableInECBM(mContext); - log("WifiController msg getConfigWiFiDisableInECBM " - + configWiFiDisableInECBM); - if (configWiFiDisableInECBM) { - mActiveModeWarden.shutdownWifi(); - } - mEcmEntryCount = 1; - } - - /** - * Handles messages received while in EcmMode. - */ - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_EMERGENCY_CALL_STATE_CHANGED: - if (msg.arg1 == 1) { - // nothing to do - just says emergency call started - mEcmEntryCount++; - } else if (msg.arg1 == 0) { - // emergency call ended - decrementCountAndReturnToAppropriateState(); - } - return HANDLED; - case CMD_EMERGENCY_MODE_CHANGED: - if (msg.arg1 == 1) { - // Transitioned into emergency callback mode - mEcmEntryCount++; - } else if (msg.arg1 == 0) { - // out of emergency callback mode - decrementCountAndReturnToAppropriateState(); - } - return HANDLED; - case CMD_RECOVERY_RESTART_WIFI: - case CMD_RECOVERY_DISABLE_WIFI: - // do not want to restart wifi if we are in emergency mode - return HANDLED; - case CMD_AP_STOPPED: - case CMD_SCANNING_STOPPED: - case CMD_STA_STOPPED: - // do not want to trigger a mode switch if we are in emergency mode - return HANDLED; - case CMD_SET_AP: - // do not want to start softap if we are in emergency mode - return HANDLED; - default: - return NOT_HANDLED; - } - } - - private void decrementCountAndReturnToAppropriateState() { - boolean exitEcm = false; - - if (mEcmEntryCount == 0) { - loge("mEcmEntryCount is 0; exiting Ecm"); - exitEcm = true; - } else if (--mEcmEntryCount == 0) { - exitEcm = true; - } - - if (exitEcm) { - if (mSettingsStore.isWifiToggleEnabled()) { - transitionTo(mStaEnabledState); - } else if (checkScanOnlyModeAvailable()) { - transitionTo(mStaDisabledWithScanState); - } else { - transitionTo(mStaDisabledState); - } - } - } - } -} diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java index f9d147c6b5..1d4edf319c 100644 --- a/service/java/com/android/server/wifi/WifiCountryCode.java +++ b/service/java/com/android/server/wifi/WifiCountryCode.java @@ -16,9 +16,17 @@ package com.android.server.wifi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.text.SimpleDateFormat; @@ -33,6 +41,7 @@ import java.util.Locale; */ public class WifiCountryCode { private static final String TAG = "WifiCountryCode"; + private final TelephonyManager mTelephonyManager; private final WifiNative mWifiNative; private boolean DBG = false; private boolean mReady = false; @@ -51,10 +60,12 @@ public class WifiCountryCode { private boolean mForceCountryCode = false; public WifiCountryCode( + Context context, + Handler handler, WifiNative wifiNative, String oemDefaultCountryCode, boolean revertCountryCodeOnCellularLoss) { - + mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mWifiNative = wifiNative; mRevertCountryCodeOnCellularLoss = revertCountryCodeOnCellularLoss; @@ -67,6 +78,13 @@ public class WifiCountryCode { mRevertCountryCodeOnCellularLoss = false; } } + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String countryCode = intent.getStringExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY); + Log.d(TAG, "Country code changed"); + setCountryCodeAndUpdate(countryCode); + }}, new IntentFilter(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED), null, handler); Log.d(TAG, "mDefaultCountryCode " + mDefaultCountryCode + " mRevertCountryCodeOnCellularLoss " + mRevertCountryCodeOnCellularLoss); @@ -83,6 +101,14 @@ public class WifiCountryCode { } } + private void initializeTelephonyCountryCodeIfNeeded() { + // If we don't have a country code set, read it from telephony on bootup. + if (mTelephonyCountryCode == null) { + Log.d(TAG, "Reading country code on initialization"); + setCountryCode(mTelephonyManager.getNetworkCountryIso()); + } + } + /** * Change the state to indicates if wpa_supplicant is ready to handle country code changing * request or not. @@ -95,6 +121,7 @@ public class WifiCountryCode { // We are ready to set country code now. // We need to post pending country code request. if (mReady) { + initializeTelephonyCountryCodeIfNeeded(); updateCountryCode(); } } @@ -129,19 +156,12 @@ public class WifiCountryCode { mTelephonyCountryCode = null; } - /** - * Handle country code change request. - * @param countryCode The country code intended to set. - * This is supposed to be from Telephony service. - * 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) { + private boolean setCountryCode(String countryCode) { if (mForceCountryCode) { Log.d(TAG, "Country code can't be set because it is the force-country-code mode"); return false; } - Log.d(TAG, "Receive set country code request: " + countryCode); + Log.d(TAG, "Set country code to: " + countryCode); mTelephonyCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); // Empty country code. @@ -153,6 +173,18 @@ public class WifiCountryCode { } else { mTelephonyCountryCode = countryCode.toUpperCase(Locale.US); } + return true; + } + + /** + * Handle country code change request. + * @param countryCode The country code intended to set. + * This is supposed to be from Telephony service. + * otherwise we think it is from other applications. + * @return Returns true if the country code passed in is acceptable. + */ + private boolean setCountryCodeAndUpdate(String countryCode) { + if (!setCountryCode(countryCode)) return false; // If wpa_supplicant is ready we set the country code now, otherwise it will be // set once wpa_supplicant is ready. if (mReady) { @@ -173,6 +205,7 @@ public class WifiCountryCode { * country code. * Returns null if no Country Code was sent to driver. */ + @VisibleForTesting public synchronized String getCountryCodeSentToDriver() { return mDriverCountryCode; } @@ -192,7 +225,6 @@ public class WifiCountryCode { * Method to dump the current state of this WifiCounrtyCode object. */ public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("mRevertCountryCodeOnCellularLoss: " + mRevertCountryCodeOnCellularLoss); pw.println("mDefaultCountryCode: " + mDefaultCountryCode); pw.println("mDriverCountryCode: " + mDriverCountryCode); diff --git a/service/java/com/android/server/wifi/WifiDataStall.java b/service/java/com/android/server/wifi/WifiDataStall.java index 6eb3b41e5e..e32c87c9ec 100644 --- a/service/java/com/android/server/wifi/WifiDataStall.java +++ b/service/java/com/android/server/wifi/WifiDataStall.java @@ -18,8 +18,6 @@ package com.android.server.wifi; import android.content.Context; import android.net.wifi.WifiInfo; -import android.os.Handler; -import android.os.Looper; import android.provider.Settings; import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; @@ -48,14 +46,8 @@ public class WifiDataStall { private final FrameworkFacade mFacade; private final WifiMetrics mWifiMetrics; - private Handler mHandler; private int mMinTxBad; private int mMinTxSuccessWithoutRx; - private int mDataStallDurationMs; - private int mDataStallTxTputThrMbps; - private int mDataStallRxTputThrMbps; - private int mDataStallTxPerThr; - private int mDataStallCcaLevelThr; private int mLastFrequency = -1; private String mLastBssid; private long mLastTotalRadioOnFreqTimeMs = -1; @@ -66,20 +58,13 @@ public class WifiDataStall { private boolean mDataStallRx = false; public WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics, - DeviceConfigFacade deviceConfigFacade, Looper clientModeImplLooper, Clock clock) { + DeviceConfigFacade deviceConfigFacade, Clock clock) { mContext = context; mDeviceConfigFacade = deviceConfigFacade; mFacade = facade; - mHandler = new Handler(clientModeImplLooper); mWifiMetrics = wifiMetrics; mClock = clock; loadSettings(); - - mDeviceConfigFacade.addOnPropertiesChangedListener( - command -> mHandler.post(command), - properties -> { - updateUsabilityDataCollectionFlags(); - }); } /** @@ -93,7 +78,6 @@ public class WifiDataStall { MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT); mWifiMetrics.setWifiDataStallMinTxBad(mMinTxBad); mWifiMetrics.setWifiDataStallMinRxWithoutTx(mMinTxSuccessWithoutRx); - updateUsabilityDataCollectionFlags(); } /** @@ -140,7 +124,6 @@ public class WifiDataStall { mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta, txBadDelta, rxSuccessDelta, timeMsDelta); - if (timeMsDelta < MAX_MS_DELTA_FOR_DATA_STALL) { int txLinkSpeed = wifiInfo.getLinkSpeed(); int rxLinkSpeed = wifiInfo.getRxLinkSpeedMbps(); @@ -156,17 +139,20 @@ public class WifiDataStall { boolean isTxTputLow = false; boolean isRxTputLow = false; if (txLinkSpeed > 0) { - int txTput = txLinkSpeed * (100 - txPer) * (100 - ccaLevel); - isTxTputLow = txTput < mDataStallTxTputThrMbps * 100 * 100; + long txTputKbps = (long) txLinkSpeed * 1000 * (100 - txPer) * (100 - ccaLevel); + isTxTputLow = + txTputKbps < mDeviceConfigFacade.getDataStallTxTputThrKbps() * 100 * 100; } if (rxLinkSpeed > 0) { - int rxTput = rxLinkSpeed * (100 - ccaLevel); - isRxTputLow = rxTput < mDataStallRxTputThrMbps * 100; + long rxTputKbps = (long) rxLinkSpeed * 1000 * (100 - ccaLevel); + isRxTputLow = rxTputKbps < mDeviceConfigFacade.getDataStallRxTputThrKbps() * 100; } - boolean dataStallTx = isTxTputLow || ccaLevel >= mDataStallCcaLevelThr - || txPer >= mDataStallTxPerThr; - boolean dataStallRx = isRxTputLow || ccaLevel >= mDataStallCcaLevelThr; + boolean dataStallTx = isTxTputLow + || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr() + || txPer >= mDeviceConfigFacade.getDataStallTxPerThr(); + boolean dataStallRx = isRxTputLow + || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr(); // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall @@ -175,7 +161,7 @@ public class WifiDataStall { mDataStallRx = mDataStallRx || dataStallRx; if (mDataStallStartTimeMs == -1) { mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis(); - if (mDataStallDurationMs == 0) { + if (mDeviceConfigFacade.getDataStallDurationMs() == 0) { mDataStallStartTimeMs = -1; int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx); mDataStallRx = false; @@ -184,7 +170,7 @@ public class WifiDataStall { } } else { long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs; - if (elapsedTime >= mDataStallDurationMs) { + if (elapsedTime >= mDeviceConfigFacade.getDataStallDurationMs()) { mDataStallStartTimeMs = -1; if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) { int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx); @@ -252,18 +238,4 @@ public class WifiDataStall { mWifiMetrics.logWifiIsUnusableEvent(result); return result; } - - private void updateUsabilityDataCollectionFlags() { - mDataStallDurationMs = mDeviceConfigFacade.getDataStallDurationMs(); - mDataStallTxTputThrMbps = mDeviceConfigFacade.getDataStallTxTputThrMbps(); - mDataStallRxTputThrMbps = mDeviceConfigFacade.getDataStallRxTputThrMbps(); - mDataStallTxPerThr = mDeviceConfigFacade.getDataStallTxPerThr(); - mDataStallCcaLevelThr = mDeviceConfigFacade.getDataStallCcaLevelThr(); - - mWifiMetrics.setDataStallDurationMs(mDataStallDurationMs); - mWifiMetrics.setDataStallTxTputThrMbps(mDataStallTxTputThrMbps); - mWifiMetrics.setDataStallRxTputThrMbps(mDataStallRxTputThrMbps); - mWifiMetrics.setDataStallTxPerThr(mDataStallTxPerThr); - mWifiMetrics.setDataStallCcaLevelThr(mDataStallCcaLevelThr); - } } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index fe9ebea17c..19bdf2eca3 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -21,14 +21,12 @@ import android.app.ActivityManager; import android.app.AlarmManager; import android.app.AppOpsManager; import android.content.Context; -import android.content.pm.PackageManager; import android.hardware.SystemSensorManager; import android.net.IpMemoryStore; import android.net.NetworkCapabilities; import android.net.NetworkKey; import android.net.NetworkScoreManager; import android.net.wifi.IWifiScanner; -import android.net.wifi.IWificond; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiScanner; @@ -63,6 +61,7 @@ import com.android.server.wifi.p2p.WifiP2pNative; import com.android.server.wifi.rtt.RttMetrics; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; +import com.android.server.wifi.wificond.IWificond; import java.util.Random; @@ -83,9 +82,11 @@ public class WifiInjector { private final Context mContext; private final FrameworkFacade mFrameworkFacade = new FrameworkFacade(); private final DeviceConfigFacade mDeviceConfigFacade; - private final HandlerThread mWifiServiceHandlerThread; - private final HandlerThread mWifiCoreHandlerThread; + private final UserManager mUserManager; + private final HandlerThread mAsyncChannelHandlerThread; + private final HandlerThread mWifiHandlerThread; private final HandlerThread mWifiP2pServiceHandlerThread; + private final HandlerThread mPasspointProvisionerHandlerThread; private final WifiTrafficPoller mWifiTrafficPoller; private final WifiCountryCode mCountryCode; private final BackupManagerProxy mBackupManagerProxy = new BackupManagerProxy(); @@ -103,10 +104,8 @@ public class WifiInjector { private final ActiveModeWarden mActiveModeWarden; private final WifiSettingsStore mSettingsStore; private OpenNetworkNotifier mOpenNetworkNotifier; - private CarrierNetworkNotifier mCarrierNetworkNotifier; private final CarrierNetworkConfig mCarrierNetworkConfig; private final WifiLockManager mLockManager; - private final WifiController mWifiController; private final WificondControl mWificondControl; private final Clock mClock = new Clock(); private final WifiMetrics mWifiMetrics; @@ -152,8 +151,8 @@ public class WifiInjector { private final DppMetrics mDppMetrics; private final DppManager mDppManager; private final LinkProbeManager mLinkProbeManager; - private final IpMemoryStore mIpMemoryStore; - private final CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector; + private IpMemoryStore mIpMemoryStore; + private final WifiThreadRunner mWifiThreadRunner; public WifiInjector(Context context) { if (context == null) { @@ -169,7 +168,6 @@ public class WifiInjector { sWifiInjector = this; mContext = context; - mDeviceConfigFacade = new DeviceConfigFacade(); mWifiScoreCard = new WifiScoreCard(mClock, Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID)); mSettingsStore = new WifiSettingsStore(mContext); @@ -178,48 +176,50 @@ public class WifiInjector { mWifiNetworkScoreCache = new WifiNetworkScoreCache(mContext); mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE); + mUserManager = mContext.getSystemService(UserManager.class); mWifiPermissionsUtil = new WifiPermissionsUtil(mWifiPermissionsWrapper, mContext, - UserManager.get(mContext), this); + mUserManager, 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(); - mWifiCoreHandlerThread = new HandlerThread("ClientModeImpl"); - mWifiCoreHandlerThread.start(); + mAsyncChannelHandlerThread = new HandlerThread("AsyncChannelHandlerThread"); + mAsyncChannelHandlerThread.start(); + mWifiHandlerThread = new HandlerThread("WifiHandlerThread"); + mWifiHandlerThread.start(); + Looper wifiLooper = mWifiHandlerThread.getLooper(); + Handler wifiHandler = mWifiHandlerThread.getThreadHandler(); + mWifiThreadRunner = new WifiThreadRunner(wifiHandler); mWifiP2pServiceHandlerThread = new HandlerThread("WifiP2pService"); mWifiP2pServiceHandlerThread.start(); - Looper clientModeImplLooper = mWifiCoreHandlerThread.getLooper(); - mCarrierNetworkConfig = new CarrierNetworkConfig(mContext, - clientModeImplLooper, mFrameworkFacade); + mPasspointProvisionerHandlerThread = + new HandlerThread("PasspointProvisionerHandlerThread"); + mPasspointProvisionerHandlerThread.start(); + mCarrierNetworkConfig = new CarrierNetworkConfig(mContext, wifiHandler, mFrameworkFacade); WifiAwareMetrics awareMetrics = new WifiAwareMetrics(mClock); RttMetrics rttMetrics = new RttMetrics(mClock); mWifiP2pMetrics = new WifiP2pMetrics(mClock); mDppMetrics = new DppMetrics(); - mCellularLinkLayerStatsCollector = new CellularLinkLayerStatsCollector(mContext); - mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, clientModeImplLooper, - awareMetrics, rttMetrics, new WifiPowerMetrics(), mWifiP2pMetrics, mDppMetrics, - mCellularLinkLayerStatsCollector); + mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, wifiLooper, + awareMetrics, rttMetrics, new WifiPowerMetrics(), mWifiP2pMetrics, mDppMetrics); + mDeviceConfigFacade = new DeviceConfigFacade(mContext, wifiHandler, mWifiMetrics); // Modules interacting with Native. mWifiMonitor = new WifiMonitor(this); - mHalDeviceManager = new HalDeviceManager(mClock, clientModeImplLooper); - mWifiVendorHal = - new WifiVendorHal(mHalDeviceManager, mWifiCoreHandlerThread.getLooper()); - mSupplicantStaIfaceHal = - new SupplicantStaIfaceHal(mContext, mWifiMonitor, mPropertyService, - clientModeImplLooper); - mHostapdHal = new HostapdHal(mContext, clientModeImplLooper); + mHalDeviceManager = new HalDeviceManager(mClock, wifiHandler); + mWifiVendorHal = new WifiVendorHal(mHalDeviceManager, wifiHandler); + mSupplicantStaIfaceHal = new SupplicantStaIfaceHal( + mContext, mWifiMonitor, mPropertyService, wifiHandler, mClock); + mHostapdHal = new HostapdHal(mContext, wifiHandler); mWificondControl = new WificondControl(this, mWifiMonitor, mCarrierNetworkConfig, (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE), - clientModeImplLooper, mClock); + wifiHandler, mClock); mNwManagementService = INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); mWifiNative = new WifiNative( mWifiVendorHal, mSupplicantStaIfaceHal, mHostapdHal, mWificondControl, mWifiMonitor, mNwManagementService, mPropertyService, mWifiMetrics, - new Handler(mWifiCoreHandlerThread.getLooper()), new Random()); + wifiHandler, new Random()); mWifiP2pMonitor = new WifiP2pMonitor(this); mSupplicantP2pIfaceHal = new SupplicantP2pIfaceHal(mWifiP2pMonitor); mWifiP2pNative = new WifiP2pNative( @@ -227,36 +227,34 @@ public class WifiInjector { mPropertyService); // Now get instances of all the objects that depend on the HandlerThreads - mWifiTrafficPoller = new WifiTrafficPoller(clientModeImplLooper); - mCountryCode = new WifiCountryCode(mWifiNative, + mWifiTrafficPoller = new WifiTrafficPoller(wifiHandler); + mCountryCode = new WifiCountryCode(mContext, wifiHandler, 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, mWifiCoreHandlerThread.getLooper(), mBackupManagerProxy, - mFrameworkFacade); + mContext,this, wifiHandler, mBackupManagerProxy, mFrameworkFacade); // WifiConfigManager/Store objects and their dependencies. // New config store mWifiKeyStore = new WifiKeyStore(mKeyStore); - mWifiConfigStore = new WifiConfigStore( - mContext, clientModeImplLooper, mClock, mWifiMetrics, + mWifiConfigStore = new WifiConfigStore(mContext, wifiHandler, mClock, mWifiMetrics, WifiConfigStore.createSharedFile()); SubscriptionManager subscriptionManager = mContext.getSystemService(SubscriptionManager.class); // Config Manager mWifiConfigManager = new WifiConfigManager(mContext, mClock, - UserManager.get(mContext), makeTelephonyManager(), + mUserManager, makeTelephonyManager(), mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil, mWifiPermissionsWrapper, this, new NetworkListSharedStoreData(mContext), new NetworkListUserStoreData(mContext), new DeletedEphemeralSsidsStoreData(mClock), new RandomizedMacStoreData(), - mFrameworkFacade, mWifiCoreHandlerThread.getLooper()); + mFrameworkFacade, wifiHandler, mDeviceConfigFacade); mWifiMetrics.setWifiConfigManager(mWifiConfigManager); mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative); - mConnectivityLocalLog = new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 256 : 512); - mScoringParams = new ScoringParams(mContext, mFrameworkFacade, - new Handler(clientModeImplLooper)); + mConnectivityLocalLog = new LocalLog( + mContext.getSystemService(ActivityManager.class).isLowRamDevice() ? 256 : 512); + mScoringParams = new ScoringParams(mContext, mFrameworkFacade, wifiHandler); mWifiMetrics.setScoringParams(mScoringParams); mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiScoreCard, mScoringParams, mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiMetrics, mWifiNative); @@ -270,19 +268,18 @@ public class WifiInjector { mSavedNetworkEvaluator = new SavedNetworkEvaluator(mContext, mScoringParams, mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiConnectivityHelper, subscriptionManager); - mWifiNetworkSuggestionsManager = new WifiNetworkSuggestionsManager(mContext, - new Handler(mWifiCoreHandlerThread.getLooper()), this, - mWifiPermissionsUtil, mWifiConfigManager, mWifiConfigStore, mWifiMetrics); + mWifiNetworkSuggestionsManager = new WifiNetworkSuggestionsManager(mContext, wifiHandler, + this, mWifiPermissionsUtil, mWifiConfigManager, mWifiConfigStore, mWifiMetrics); mNetworkSuggestionEvaluator = new NetworkSuggestionEvaluator(mWifiNetworkSuggestionsManager, mWifiConfigManager, mConnectivityLocalLog); - mScoredNetworkEvaluator = new ScoredNetworkEvaluator(context, clientModeImplLooper, + mScoredNetworkEvaluator = new ScoredNetworkEvaluator(context, wifiHandler, mFrameworkFacade, mNetworkScoreManager, mWifiConfigManager, mConnectivityLocalLog, mWifiNetworkScoreCache, mWifiPermissionsUtil); mCarrierNetworkEvaluator = new CarrierNetworkEvaluator(mWifiConfigManager, mCarrierNetworkConfig, mConnectivityLocalLog, this); mSimAccessor = new SIMAccessor(mContext); mPasspointManager = new PasspointManager(mContext, this, - new Handler(mWifiCoreHandlerThread.getLooper()), mWifiNative, mWifiKeyStore, mClock, + wifiHandler, mWifiNative, mWifiKeyStore, mClock, mSimAccessor, new PasspointObjectFactory(), mWifiConfigManager, mWifiConfigStore, mWifiMetrics, makeTelephonyManager(), subscriptionManager); mPasspointNetworkEvaluator = new PasspointNetworkEvaluator( @@ -293,59 +290,49 @@ public class WifiInjector { (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE), (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE), this, mWifiConfigManager, - mWifiPermissionsUtil, mWifiMetrics, mClock, mFrameworkFacade, - new Handler(clientModeImplLooper)); - mSarManager = new SarManager(mContext, makeTelephonyManager(), clientModeImplLooper, - mWifiNative, new SystemSensorManager(mContext, clientModeImplLooper), + mWifiPermissionsUtil, mWifiMetrics, mClock, mFrameworkFacade, wifiHandler); + mSarManager = new SarManager(mContext, makeTelephonyManager(), wifiLooper, + mWifiNative, new SystemSensorManager(mContext, wifiLooper), mWifiMetrics); mWifiDiagnostics = new WifiDiagnostics( mContext, this, mWifiNative, mBuildProperties, new LastMileLogger(this), mClock); mWifiDataStall = new WifiDataStall(mContext, mFrameworkFacade, mWifiMetrics, - mDeviceConfigFacade, clientModeImplLooper, mClock); + mDeviceConfigFacade, mClock); mWifiMetrics.setWifiDataStall(mWifiDataStall); mLinkProbeManager = new LinkProbeManager(mClock, mWifiNative, mWifiMetrics, - mFrameworkFacade, mWifiCoreHandlerThread.getLooper(), mContext); + mFrameworkFacade, wifiHandler, mContext); mClientModeImpl = new ClientModeImpl(mContext, mFrameworkFacade, - clientModeImplLooper, UserManager.get(mContext), + wifiLooper, mUserManager, this, mBackupManagerProxy, mCountryCode, mWifiNative, new WrongPasswordNotifier(mContext, mFrameworkFacade), mSarManager, mWifiTrafficPoller, mLinkProbeManager); - mActiveModeWarden = new ActiveModeWarden(this, mContext, clientModeImplLooper, - mWifiNative, new DefaultModeManager(mContext, clientModeImplLooper), - mBatteryStats); + mActiveModeWarden = new ActiveModeWarden(this, wifiLooper, + mWifiNative, new DefaultModeManager(mContext), mBatteryStats, mWifiDiagnostics, + mContext, mClientModeImpl, mSettingsStore, mFrameworkFacade, mWifiPermissionsUtil); WakeupNotificationFactory wakeupNotificationFactory = - new WakeupNotificationFactory(mContext, mFrameworkFacade); + new WakeupNotificationFactory(mContext, this, mFrameworkFacade); WakeupOnboarding wakeupOnboarding = new WakeupOnboarding(mContext, mWifiConfigManager, - mWifiCoreHandlerThread.getLooper(), mFrameworkFacade, - wakeupNotificationFactory); - mWakeupController = new WakeupController(mContext, - mWifiCoreHandlerThread.getLooper(), + wifiHandler, mFrameworkFacade, wakeupNotificationFactory); + mWakeupController = new WakeupController(mContext, wifiHandler, new WakeupLock(mWifiConfigManager, mWifiMetrics.getWakeupMetrics(), mClock), new WakeupEvaluator(mScoringParams), wakeupOnboarding, mWifiConfigManager, mWifiConfigStore, mWifiNetworkSuggestionsManager, mWifiMetrics.getWakeupMetrics(), this, mFrameworkFacade, mClock); mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService(), - mClientModeImpl, mFrameworkFacade, new Handler(clientModeImplLooper), mWifiNative, - mClock, mWifiMetrics); - mWifiController = new WifiController(mContext, mClientModeImpl, clientModeImplLooper, - mSettingsStore, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade, - mActiveModeWarden, mWifiPermissionsUtil); - mSelfRecovery = new SelfRecovery(mWifiController, mClock); + mClientModeImpl, mFrameworkFacade, wifiHandler, mWifiNative, mClock, mWifiMetrics); + mSelfRecovery = new SelfRecovery(mActiveModeWarden, mClock); mWifiMulticastLockManager = new WifiMulticastLockManager( mClientModeImpl.getMcastLockManagerFilterController(), BatteryStatsService.getService()); - mDppManager = new DppManager(mWifiCoreHandlerThread.getLooper(), mWifiNative, + mDppManager = new DppManager(wifiHandler, mWifiNative, mWifiConfigManager, mContext, mDppMetrics); - mIpMemoryStore = IpMemoryStore.getMemoryStore(mContext); // Register the various network evaluators with the network selector. mWifiNetworkSelector.registerNetworkEvaluator(mSavedNetworkEvaluator); mWifiNetworkSelector.registerNetworkEvaluator(mNetworkSuggestionEvaluator); - if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)) { - mWifiNetworkSelector.registerNetworkEvaluator(mPasspointNetworkEvaluator); - } + mWifiNetworkSelector.registerNetworkEvaluator(mPasspointNetworkEvaluator); mWifiNetworkSelector.registerNetworkEvaluator(mCarrierNetworkEvaluator); mWifiNetworkSelector.registerNetworkEvaluator(mScoredNetworkEvaluator); @@ -383,7 +370,7 @@ public class WifiInjector { } public UserManager getUserManager() { - return UserManager.get(mContext); + return mUserManager; } public WifiMetrics getWifiMetrics() { @@ -406,16 +393,20 @@ public class WifiInjector { return mFrameworkFacade; } - public HandlerThread getWifiServiceHandlerThread() { - return mWifiServiceHandlerThread; + public HandlerThread getAsyncChannelHandlerThread() { + return mAsyncChannelHandlerThread; } public HandlerThread getWifiP2pServiceHandlerThread() { return mWifiP2pServiceHandlerThread; } - public HandlerThread getWifiCoreHandlerThread() { - return mWifiCoreHandlerThread; + public HandlerThread getPasspointProvisionerHandlerThread() { + return mPasspointProvisionerHandlerThread; + } + + public HandlerThread getWifiHandlerThread() { + return mWifiHandlerThread; } public WifiTrafficPoller getWifiTrafficPoller() { @@ -438,10 +429,6 @@ public class WifiInjector { return mClientModeImpl; } - public Handler getClientModeImplHandler() { - return mClientModeImpl.getHandler(); - } - public ActiveModeWarden getActiveModeWarden() { return mActiveModeWarden; } @@ -454,10 +441,6 @@ public class WifiInjector { return mLockManager; } - public WifiController getWifiController() { - return mWifiController; - } - public WifiLastResortWatchdog getWifiLastResortWatchdog() { return mWifiLastResortWatchdog; } @@ -534,35 +517,24 @@ public class WifiInjector { * @param config SoftApModeConfiguration object holding the config and mode * @return an instance of SoftApManager */ - public SoftApManager makeSoftApManager(@NonNull WifiManager.SoftApCallback callback, + public SoftApManager makeSoftApManager(@NonNull ActiveModeManager.Listener listener, + @NonNull WifiManager.SoftApCallback callback, @NonNull SoftApModeConfiguration config) { - return new SoftApManager(mContext, mWifiCoreHandlerThread.getLooper(), - mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), callback, + return new SoftApManager(mContext, mWifiHandlerThread.getLooper(), + mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), listener, callback, mWifiApConfigStore, config, mWifiMetrics, mSarManager, mWifiDiagnostics); } /** - * Create a ScanOnlyModeManager - * - * @param listener listener for ScanOnlyModeManager state changes - * @return a new instance of ScanOnlyModeManager - */ - public ScanOnlyModeManager makeScanOnlyModeManager( - @NonNull ScanOnlyModeManager.Listener listener) { - return new ScanOnlyModeManager(mContext, mWifiCoreHandlerThread.getLooper(), - mWifiNative, listener, mWifiMetrics, mWakeupController, - mSarManager); - } - - /** * Create a ClientModeManager * * @param listener listener for ClientModeManager state changes * @return a new instance of ClientModeManager */ public ClientModeManager makeClientModeManager(ClientModeManager.Listener listener) { - return new ClientModeManager(mContext, mWifiCoreHandlerThread.getLooper(), - mWifiNative, listener, mWifiMetrics, mClientModeImpl); + return new ClientModeManager(mContext, mWifiHandlerThread.getLooper(), + mWifiNative, listener, mWifiMetrics, mSarManager, mWakeupController, + mClientModeImpl); } /** @@ -587,7 +559,7 @@ public class WifiInjector { mWifiScanner = new WifiScanner(mContext, IWifiScanner.Stub.asInterface(ServiceManager.getService( Context.WIFI_SCANNING_SERVICE)), - mWifiCoreHandlerThread.getLooper()); + mWifiHandlerThread.getLooper()); } return mWifiScanner; } @@ -601,22 +573,18 @@ public class WifiInjector { */ public WifiConnectivityManager makeWifiConnectivityManager(ClientModeImpl clientModeImpl) { mOpenNetworkNotifier = new OpenNetworkNotifier(mContext, - mWifiCoreHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, + mWifiHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, mWifiConfigManager, mWifiConfigStore, clientModeImpl, - new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade)); - mCarrierNetworkNotifier = new CarrierNetworkNotifier(mContext, - mWifiCoreHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, - mWifiConfigManager, mWifiConfigStore, clientModeImpl, - new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade)); + new ConnectToNetworkNotificationBuilder(mContext, this, mFrameworkFacade)); mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mContext, mClock, - mWifiMetrics, clientModeImpl, clientModeImpl.getHandler().getLooper(), - mDeviceConfigFacade); + mWifiMetrics, clientModeImpl, mWifiHandlerThread.getLooper(), mDeviceConfigFacade, + mWifiThreadRunner); return new WifiConnectivityManager(mContext, getScoringParams(), clientModeImpl, this, mWifiConfigManager, clientModeImpl.getWifiInfo(), mWifiNetworkSelector, mWifiConnectivityHelper, - mWifiLastResortWatchdog, mOpenNetworkNotifier, mCarrierNetworkNotifier, - mCarrierNetworkConfig, mWifiMetrics, mWifiCoreHandlerThread.getLooper(), + mWifiLastResortWatchdog, mOpenNetworkNotifier, + mCarrierNetworkConfig, mWifiMetrics, mWifiHandlerThread.getThreadHandler(), mClock, mConnectivityLocalLog); } @@ -627,7 +595,7 @@ public class WifiInjector { public WifiNetworkFactory makeWifiNetworkFactory( NetworkCapabilities nc, WifiConnectivityManager wifiConnectivityManager) { return new WifiNetworkFactory( - mWifiCoreHandlerThread.getLooper(), mContext, nc, + mWifiHandlerThread.getLooper(), mContext, nc, (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE), (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE), (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE), @@ -650,7 +618,7 @@ public class WifiInjector { public UntrustedWifiNetworkFactory makeUntrustedWifiNetworkFactory( NetworkCapabilities nc, WifiConnectivityManager wifiConnectivityManager) { return new UntrustedWifiNetworkFactory( - mWifiCoreHandlerThread.getLooper(), mContext, nc, wifiConnectivityManager); + mWifiHandlerThread.getLooper(), mContext, nc, wifiConnectivityManager); } /** @@ -747,10 +715,21 @@ public class WifiInjector { } public IpMemoryStore getIpMemoryStore() { + if (mIpMemoryStore == null) { + mIpMemoryStore = IpMemoryStore.getMemoryStore(mContext); + } return mIpMemoryStore; } public HostapdHal getHostapdHal() { return mHostapdHal; } + + public String getWifiStackPackageName() { + return mContext.getPackageName(); + } + + public WifiThreadRunner getWifiThreadRunner() { + return mWifiThreadRunner; + } } diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java index e8a06881bd..18f447aac1 100644 --- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java +++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java @@ -32,9 +32,11 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; /** * This Class is a Work-In-Progress, intended behavior is as follows: @@ -77,9 +79,6 @@ public class WifiLastResortWatchdog { @VisibleForTesting public static final long LAST_TRIGGER_TIMEOUT_MILLIS = 2 * 3600 * 1000; // 2 hours - private int mAbnormalConnectionDurationMs; - private boolean mAbnormalConnectionBugreportEnabled; - /** * Cached WifiConfigurations of available networks seen within MAX_BSSID_AGE scan results @@ -94,6 +93,9 @@ public class WifiLastResortWatchdog { private Map<String, Pair<AvailableNetworkFailureCount, Integer>> mSsidFailureCount = new HashMap<>(); + /* List of failure BSSID */ + private Set<String> mBssidFailureList = new HashSet<>(); + // Tracks: if ClientModeImpl is in ConnectedState private boolean mWifiIsConnected = false; // Is Watchdog allowed to trigger now? Set to false after triggering. Set to true after @@ -115,6 +117,9 @@ public class WifiLastResortWatchdog { private boolean mWatchdogFixedWifi = true; private long mLastStartConnectTime = 0; private Handler mHandler; + private final WifiThreadRunner mWifiThreadRunner; + + private boolean mWatchdogFeatureEnabled = true; /** * Local log used for debugging any WifiLastResortWatchdog issues. @@ -123,7 +128,7 @@ public class WifiLastResortWatchdog { WifiLastResortWatchdog(WifiInjector wifiInjector, Context context, Clock clock, WifiMetrics wifiMetrics, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, - DeviceConfigFacade deviceConfigFacade) { + DeviceConfigFacade deviceConfigFacade, WifiThreadRunner wifiThreadRunner) { mWifiInjector = wifiInjector; mClock = clock; mWifiMetrics = wifiMetrics; @@ -131,29 +136,12 @@ public class WifiLastResortWatchdog { mClientModeImplLooper = clientModeImplLooper; mContext = context; mDeviceConfigFacade = deviceConfigFacade; - updateDeviceConfigFlags(); + mWifiThreadRunner = wifiThreadRunner; mHandler = new Handler(clientModeImplLooper) { public void handleMessage(Message msg) { processMessage(msg); } }; - - mDeviceConfigFacade.addOnPropertiesChangedListener( - command -> mHandler.post(command), - properties -> { - updateDeviceConfigFlags(); - }); - } - - private void updateDeviceConfigFlags() { - mAbnormalConnectionBugreportEnabled = - mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled(); - mAbnormalConnectionDurationMs = - mDeviceConfigFacade.getAbnormalConnectionDurationMs(); - logv("updateDeviceConfigFlags: mAbnormalConnectionDurationMs = " - + mAbnormalConnectionDurationMs - + ", mAbnormalConnectionBugreportEnabled = " - + mAbnormalConnectionBugreportEnabled); } /** @@ -177,17 +165,19 @@ public class WifiLastResortWatchdog { switch (msg.what) { case WifiMonitor.NETWORK_CONNECTION_EVENT: // Trigger bugreport for successful connections that take abnormally long - if (mAbnormalConnectionBugreportEnabled && mLastStartConnectTime > 0) { + if (mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled() + && mLastStartConnectTime > 0) { long durationMs = mClock.getElapsedSinceBootMillis() - mLastStartConnectTime; - if (durationMs > mAbnormalConnectionDurationMs) { + long abnormalConnectionDurationMs = + mDeviceConfigFacade.getAbnormalConnectionDurationMs(); + if (durationMs > abnormalConnectionDurationMs) { final String bugTitle = "Wi-Fi Bugreport: Abnormal connection time"; final String bugDetail = "Expected connection to take less than " - + mAbnormalConnectionDurationMs + " milliseconds. " + + abnormalConnectionDurationMs + " milliseconds. " + "Actually took " + durationMs + " milliseconds."; logv("Triggering bug report for abnormal connection time."); - mWifiInjector.getClientModeImplHandler().post(() -> { - mClientModeImpl.takeBugReport(bugTitle, bugDetail); - }); + mWifiThreadRunner.post(() -> + mClientModeImpl.takeBugReport(bugTitle, bugDetail)); } } // Should reset last connection time after each connection regardless if bugreport @@ -208,11 +198,11 @@ public class WifiLastResortWatchdog { */ public void updateAvailableNetworks( List<Pair<ScanDetail, WifiConfiguration>> availableNetworks) { - if (mVerboseLoggingEnabled) { - Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size()); - } // Add new networks to mRecentAvailableNetworks if (availableNetworks != null) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size()); + } for (Pair<ScanDetail, WifiConfiguration> pair : availableNetworks) { final ScanDetail scanDetail = pair.first; final WifiConfiguration config = pair.second; @@ -240,9 +230,9 @@ public class WifiLastResortWatchdog { 1); // Do not re-enable Watchdog in LAST_TRIGGER_TIMEOUT_MILLIS // after last time Watchdog be triggered - if (mTimeLastTrigger == 0 + if (!mWatchdogAllowedToTrigger && (mTimeLastTrigger == 0 || (mClock.getElapsedSinceBootMillis() - mTimeLastTrigger) - >= LAST_TRIGGER_TIMEOUT_MILLIS) { + >= LAST_TRIGGER_TIMEOUT_MILLIS)) { localLog("updateAvailableNetworks: setWatchdogTriggerEnabled to true"); setWatchdogTriggerEnabled(true); } @@ -320,17 +310,23 @@ public class WifiLastResortWatchdog { Log.v(TAG, "isRestartNeeded = " + isRestartNeeded); } if (isRestartNeeded) { - // Stop the watchdog from triggering until re-enabled - localLog("noteConnectionFailureAndTriggerIfNeeded: setWatchdogTriggerEnabled to false"); - setWatchdogTriggerEnabled(false); - mWatchdogFixedWifi = true; - loge("Watchdog triggering recovery"); - mSsidLastTrigger = ssid; - mTimeLastTrigger = mClock.getElapsedSinceBootMillis(); - localLog(toString()); - mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - incrementWifiMetricsTriggerCounts(); - clearAllFailureCounts(); + if (mWatchdogFeatureEnabled) { + // Stop the watchdog from triggering until re-enabled + localLog("Trigger recovery: setWatchdogTriggerEnabled to false"); + setWatchdogTriggerEnabled(false); + mWatchdogFixedWifi = true; + loge("Watchdog triggering recovery"); + mSsidLastTrigger = ssid; + mTimeLastTrigger = mClock.getElapsedSinceBootMillis(); + localLog(toString()); + mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + incrementWifiMetricsTriggerCounts(); + } else { + // auto bugreport if issue happens + loge("bugreport notification"); + setWatchdogTriggerEnabled(false); + takeBugReportWithCurrentProbability("Wifi Watchdog bite"); + } } return isRestartNeeded; } @@ -348,8 +344,10 @@ public class WifiLastResortWatchdog { return; } if (!mWatchdogAllowedToTrigger && mWatchdogFixedWifi + && mWatchdogFeatureEnabled && checkIfAtleastOneNetworkHasEverConnected() - && checkIfConnectedBackToSameSsid()) { + && checkIfConnectedBackToSameSsid() + && checkIfConnectedBssidHasEverFailed()) { takeBugReportWithCurrentProbability("Wifi fixed after restart"); // WiFi has connected after a Watchdog trigger, without any new networks becoming // available, log a Watchdog success in wifi metrics @@ -357,8 +355,6 @@ public class WifiLastResortWatchdog { long durationMs = mClock.getElapsedSinceBootMillis() - mTimeLastTrigger; mWifiMetrics.setWatchdogSuccessTimeDurationMs(durationMs); } - // We connected to something! Reset failure counts for everything - clearAllFailureCounts(); // If the watchdog trigger was disabled (it triggered), connecting means we did // something right, re-enable it so it can fire again. localLog("connectedStateTransition: setWatchdogTriggerEnabled to true"); @@ -366,6 +362,14 @@ public class WifiLastResortWatchdog { } /** + * Helper function to check if device connected to BSSID + * which is in BSSID failure list after watchdog trigger. + */ + private boolean checkIfConnectedBssidHasEverFailed() { + return mBssidFailureList.contains(mClientModeImpl.getWifiInfo().getBSSID()); + } + + /** * Helper function to check if device connect back to same * SSID after watchdog trigger */ @@ -408,6 +412,7 @@ public class WifiLastResortWatchdog { // Bssid count is actually unused except for logging purposes // SSID count is incremented within the BSSID counting method incrementBssidFailureCount(ssid, bssid, reason); + mBssidFailureList.add(bssid); } } @@ -587,7 +592,7 @@ public class WifiLastResortWatchdog { } /** - * Clear failure counts for each network in recentAvailableNetworks + * Clear all failure counts */ public void clearAllFailureCounts() { if (mVerboseLoggingEnabled) Log.v(TAG, "clearAllFailureCounts."); @@ -601,6 +606,7 @@ public class WifiLastResortWatchdog { final AvailableNetworkFailureCount failureCount = entry.getValue().first; failureCount.resetCounts(); } + mBssidFailureList.clear(); } /** * Gets the buffer of recently available networks @@ -615,6 +621,10 @@ public class WifiLastResortWatchdog { */ private void setWatchdogTriggerEnabled(boolean enable) { if (mVerboseLoggingEnabled) Log.v(TAG, "setWatchdogTriggerEnabled: enable = " + enable); + // Reset failure counts before actives watchdog + if (enable) { + clearAllFailureCounts(); + } mWatchdogAllowedToTrigger = enable; } @@ -623,7 +633,8 @@ public class WifiLastResortWatchdog { */ public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("mWatchdogAllowedToTrigger: ").append(mWatchdogAllowedToTrigger); + sb.append("mWatchdogFeatureEnabled: ").append(mWatchdogFeatureEnabled); + sb.append("\nmWatchdogAllowedToTrigger: ").append(mWatchdogAllowedToTrigger); sb.append("\nmWifiIsConnected: ").append(mWifiIsConnected); sb.append("\nmRecentAvailableNetworks: ").append(mRecentAvailableNetworks.size()); for (Map.Entry<String, AvailableNetworkFailureCount> entry @@ -682,6 +693,23 @@ public class WifiLastResortWatchdog { } } + /** + * Sets whether wifi watchdog should trigger recovery + */ + public void setWifiWatchdogFeature(boolean enable) { + logv("setWifiWatchdogFeature: " + enable); + mWatchdogFeatureEnabled = enable; + // for debugging purpose, reset mWatchdogAllowedToTrigger as well + setWatchdogTriggerEnabled(true); + } + + /** + * Returns whether wifi watchdog should trigger recovery. + */ + public boolean getWifiWatchdogFeature() { + return mWatchdogFeatureEnabled; + } + protected void enableVerboseLogging(int verbose) { if (verbose > 0) { mVerboseLoggingEnabled = true; diff --git a/service/java/com/android/server/wifi/WifiLockManager.java b/service/java/com/android/server/wifi/WifiLockManager.java index e292a84d8a..8086fc945a 100644 --- a/service/java/com/android/server/wifi/WifiLockManager.java +++ b/service/java/com/android/server/wifi/WifiLockManager.java @@ -25,7 +25,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.WorkSource; import android.os.WorkSource.WorkChain; -import android.util.Slog; +import android.util.Log; import android.util.SparseArray; import android.util.StatsLog; @@ -270,7 +270,7 @@ public class WifiLockManager { WorkSource newWorkSource = new WorkSource(ws); if (mVerboseLoggingEnabled) { - Slog.d(TAG, "updateWifiLockWakeSource: " + wl + ", newWorkSource=" + newWorkSource); + Log.d(TAG, "updateWifiLockWakeSource: " + wl + ", newWorkSource=" + newWorkSource); } // Note: @@ -309,7 +309,7 @@ public class WifiLockManager { mForceHiPerfMode = isEnabled; mForceLowLatencyMode = false; if (!updateOpMode()) { - Slog.e(TAG, "Failed to force hi-perf mode, returning to normal mode"); + Log.e(TAG, "Failed to force hi-perf mode, returning to normal mode"); mForceHiPerfMode = false; return false; } @@ -326,7 +326,7 @@ public class WifiLockManager { mForceLowLatencyMode = isEnabled; mForceHiPerfMode = false; if (!updateOpMode()) { - Slog.e(TAG, "Failed to force low-latency mode, returning to normal mode"); + Log.e(TAG, "Failed to force low-latency mode, returning to normal mode"); mForceLowLatencyMode = false; return false; } @@ -338,7 +338,7 @@ public class WifiLockManager { */ public void handleScreenStateChanged(boolean screenOn) { if (mVerboseLoggingEnabled) { - Slog.d(TAG, "handleScreenStateChanged: screenOn = " + screenOn); + Log.d(TAG, "handleScreenStateChanged: screenOn = " + screenOn); } mScreenOn = screenOn; @@ -404,7 +404,7 @@ public class WifiLockManager { mLowLatencyUidWatchList.put(uid, uidRec); // Now check if the uid is running in foreground - if (mFrameworkFacade.isAppForeground(uid)) { + if (mFrameworkFacade.isAppForeground(mContext, uid)) { uidRec.mIsFg = true; } @@ -418,14 +418,14 @@ public class WifiLockManager { private void removeUidFromLlWatchList(int uid) { UidRec uidRec = mLowLatencyUidWatchList.get(uid); if (uidRec == null) { - Slog.e(TAG, "Failed to find uid in low-latency watch list"); + Log.e(TAG, "Failed to find uid in low-latency watch list"); return; } if (uidRec.mLockCount > 0) { uidRec.mLockCount--; } else { - Slog.e(TAG, "Error, uid record conatains no locks"); + Log.e(TAG, "Error, uid record conatains no locks"); } if (uidRec.mLockCount == 0) { mLowLatencyUidWatchList.remove(uid); @@ -475,12 +475,12 @@ public class WifiLockManager { private synchronized boolean addLock(WifiLock lock) { if (mVerboseLoggingEnabled) { - Slog.d(TAG, "addLock: " + lock); + Log.d(TAG, "addLock: " + lock); } if (findLockByBinder(lock.getBinder()) != null) { if (mVerboseLoggingEnabled) { - Slog.d(TAG, "attempted to add a lock when already holding one"); + Log.d(TAG, "attempted to add a lock when already holding one"); } return false; } @@ -527,7 +527,7 @@ public class WifiLockManager { } if (mVerboseLoggingEnabled) { - Slog.d(TAG, "releaseLock: " + wifiLock); + Log.d(TAG, "releaseLock: " + wifiLock); } switch(wifiLock.mMode) { @@ -568,14 +568,14 @@ public class WifiLockManager { } if (mVerboseLoggingEnabled) { - Slog.d(TAG, "Current opMode: " + mCurrentOpMode + " New LockMode: " + newLockMode); + Log.d(TAG, "Current opMode: " + mCurrentOpMode + " New LockMode: " + newLockMode); } // Otherwise, we need to change current mode, first reset it to normal switch (mCurrentOpMode) { case WifiManager.WIFI_MODE_FULL_HIGH_PERF: if (!mClientModeImpl.setPowerSave(true)) { - Slog.e(TAG, "Failed to reset the OpMode from hi-perf to Normal"); + Log.e(TAG, "Failed to reset the OpMode from hi-perf to Normal"); return false; } mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, @@ -584,7 +584,7 @@ public class WifiLockManager { case WifiManager.WIFI_MODE_FULL_LOW_LATENCY: if (!setLowLatencyMode(false)) { - Slog.e(TAG, "Failed to reset the OpMode from low-latency to Normal"); + Log.e(TAG, "Failed to reset the OpMode from low-latency to Normal"); return false; } mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, @@ -604,7 +604,7 @@ public class WifiLockManager { switch (newLockMode) { case WifiManager.WIFI_MODE_FULL_HIGH_PERF: if (!mClientModeImpl.setPowerSave(false)) { - Slog.e(TAG, "Failed to set the OpMode to hi-perf"); + Log.e(TAG, "Failed to set the OpMode to hi-perf"); return false; } mCurrentSessionStartTimeMs = mClock.getElapsedSinceBootMillis(); @@ -612,7 +612,7 @@ public class WifiLockManager { case WifiManager.WIFI_MODE_FULL_LOW_LATENCY: if (!setLowLatencyMode(true)) { - Slog.e(TAG, "Failed to set the OpMode to low-latency"); + Log.e(TAG, "Failed to set the OpMode to low-latency"); return false; } mCurrentSessionStartTimeMs = mClock.getElapsedSinceBootMillis(); @@ -624,7 +624,7 @@ public class WifiLockManager { default: // Invalid mode, don't change currentOpMode , and exit with error - Slog.e(TAG, "Invalid new opMode: " + newLockMode); + Log.e(TAG, "Invalid new opMode: " + newLockMode); return false; } @@ -663,12 +663,12 @@ public class WifiLockManager { if (lowLatencySupport == LOW_LATENCY_SUPPORTED) { if (!mClientModeImpl.setLowLatencyMode(enabled)) { - Slog.e(TAG, "Failed to set low latency mode"); + Log.e(TAG, "Failed to set low latency mode"); return false; } if (!mClientModeImpl.setPowerSave(!enabled)) { - Slog.e(TAG, "Failed to set power save mode"); + Log.e(TAG, "Failed to set power save mode"); // Revert the low latency mode mClientModeImpl.setLowLatencyMode(!enabled); return false; @@ -676,7 +676,7 @@ public class WifiLockManager { } else if (lowLatencySupport == LOW_LATENCY_NOT_SUPPORTED) { // Only set power save mode if (!mClientModeImpl.setPowerSave(!enabled)) { - Slog.e(TAG, "Failed to set power save mode"); + Log.e(TAG, "Failed to set power save mode"); return false; } } diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 27fe140a78..a875cd6c3f 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -39,7 +39,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.SystemProperties; import android.provider.Settings; -import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.Base64; import android.util.Log; @@ -432,8 +431,6 @@ public class WifiMetrics { private int mNetworkSelectorExperimentId; - private final CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector; - /** * Tracks the nominator for each network (i.e. which entity made the suggestion to connect). * This object should not be cleared. @@ -758,8 +755,7 @@ public class WifiMetrics { public WifiMetrics(Context context, FrameworkFacade facade, Clock clock, Looper looper, WifiAwareMetrics awareMetrics, RttMetrics rttMetrics, WifiPowerMetrics wifiPowerMetrics, WifiP2pMetrics wifiP2pMetrics, - DppMetrics dppMetrics, - CellularLinkLayerStatsCollector cellularLinkLayerStatsCollector) { + DppMetrics dppMetrics) { mContext = context; mFacade = facade; mClock = clock; @@ -772,7 +768,6 @@ public class WifiMetrics { mWifiPowerMetrics = wifiPowerMetrics; mWifiP2pMetrics = wifiP2pMetrics; mDppMetrics = dppMetrics; - mCellularLinkLayerStatsCollector = cellularLinkLayerStatsCollector; loadSettings(); mHandler = new Handler(looper) { public void handleMessage(Message msg) { @@ -905,24 +900,6 @@ public class WifiMetrics { } /** - * Increment number of pno scans started successfully over offload - */ - public void incrementPnoScanStartedOverOffloadCount() { - synchronized (mLock) { - mPnoScanMetrics.numPnoScanStartedOverOffload++; - } - } - - /** - * Increment number of pno scans failed over offload - */ - public void incrementPnoScanFailedOverOffloadCount() { - synchronized (mLock) { - mPnoScanMetrics.numPnoScanFailedOverOffload++; - } - } - - /** * Increment number of times pno scan found a result */ public void incrementPnoFoundNetworkEventCount() { @@ -2266,10 +2243,6 @@ public class WifiMetrics { } } - /** - * TODO: (b/72443859) Use notifierTag param to separate metrics for OpenNetworkNotifier and - * CarrierNetworkNotifier, for this method and all other related metrics. - */ /** Increments the occurence of a "Connect to Network" notification. */ public void incrementConnectToNetworkNotification(String notifierTag, int notificationType) { synchronized (mLock) { @@ -2784,10 +2757,10 @@ public class WifiMetrics { + mExperimentValues.linkSpeedCountsLoggingEnabled); pw.println("mExperimentValues.dataStallDurationMs=" + mExperimentValues.dataStallDurationMs); - pw.println("mExperimentValues.dataStallTxTputThrMbps=" - + mExperimentValues.dataStallTxTputThrMbps); - pw.println("mExperimentValues.dataStallRxTputThrMbps=" - + mExperimentValues.dataStallRxTputThrMbps); + pw.println("mExperimentValues.dataStallTxTputThrKbps=" + + mExperimentValues.dataStallTxTputThrKbps); + pw.println("mExperimentValues.dataStallRxTputThrKbps=" + + mExperimentValues.dataStallRxTputThrKbps); pw.println("mExperimentValues.dataStallTxPerThr=" + mExperimentValues.dataStallTxPerThr); pw.println("mExperimentValues.dataStallCcaLevelThr=" @@ -2917,10 +2890,6 @@ public class WifiMetrics { line.append(",rx_link_speed_mbps=" + entry.rxLinkSpeedMbps); line.append(",seq_num_inside_framework=" + entry.seqNumInsideFramework); line.append(",is_same_bssid_and_freq=" + entry.isSameBssidAndFreq); - line.append(",cellular_data_network_type=" + entry.cellularDataNetworkType); - line.append(",cellular_signal_strength_dbm=" + entry.cellularSignalStrengthDbm); - line.append(",cellular_signal_strength_db=" + entry.cellularSignalStrengthDb); - line.append(",is_same_registered_cell=" + entry.isSameRegisteredCell); line.append(",device_mobility_state=" + entry.deviceMobilityState); pw.println(line.toString()); } @@ -3696,10 +3665,10 @@ public class WifiMetrics { StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; mSupplicantStateChangeBitmask |= supplicantStateToBit(stateChangeResult.state); break; - case ClientModeImpl.CMD_ASSOCIATED_BSSID: + case WifiMonitor.ASSOCIATED_BSSID_EVENT: event.type = StaEvent.TYPE_CMD_ASSOCIATED_BSSID; break; - case ClientModeImpl.CMD_TARGET_BSSID: + case WifiMonitor.TARGET_BSSID_EVENT: event.type = StaEvent.TYPE_CMD_TARGET_BSSID; break; default: @@ -4386,14 +4355,6 @@ public class WifiMetrics { wifiUsabilityStatsEntry.seqNumInsideFramework = mSeqNumInsideFramework; wifiUsabilityStatsEntry.deviceMobilityState = mCurrentDeviceMobilityState; - CellularLinkLayerStats cls = mCellularLinkLayerStatsCollector.update(); - if (DBG) Log.v(TAG, "Latest Cellular Link Layer Stats: " + cls); - wifiUsabilityStatsEntry.cellularDataNetworkType = - parseDataNetworkTypeToProto(cls.getDataNetworkType()); - wifiUsabilityStatsEntry.cellularSignalStrengthDbm = cls.getSignalStrengthDbm(); - wifiUsabilityStatsEntry.cellularSignalStrengthDb = cls.getSignalStrengthDb(); - wifiUsabilityStatsEntry.isSameRegisteredCell = cls.getIsSameRegisteredCell(); - mWifiUsabilityStatsEntriesList.add(wifiUsabilityStatsEntry); mWifiUsabilityStatsCounter++; if (mWifiUsabilityStatsCounter >= NUM_WIFI_USABILITY_STATS_ENTRIES_PER_WIFI_GOOD) { @@ -4423,53 +4384,6 @@ public class WifiMetrics { } } - private int parseDataNetworkTypeToProto(int cellularDataNetworkType) { - switch (cellularDataNetworkType) { - case TelephonyManager.NETWORK_TYPE_UNKNOWN: - return WifiUsabilityStatsEntry.NETWORK_TYPE_UNKNOWN; - case TelephonyManager.NETWORK_TYPE_GSM: - return WifiUsabilityStatsEntry.NETWORK_TYPE_GSM; - case TelephonyManager.NETWORK_TYPE_CDMA: - return WifiUsabilityStatsEntry.NETWORK_TYPE_CDMA; - case TelephonyManager.NETWORK_TYPE_EVDO_0: - return WifiUsabilityStatsEntry.NETWORK_TYPE_EVDO_0; - case TelephonyManager.NETWORK_TYPE_UMTS: - return WifiUsabilityStatsEntry.NETWORK_TYPE_UMTS; - case TelephonyManager.NETWORK_TYPE_TD_SCDMA: - return WifiUsabilityStatsEntry.NETWORK_TYPE_TD_SCDMA; - case TelephonyManager.NETWORK_TYPE_LTE: - return WifiUsabilityStatsEntry.NETWORK_TYPE_LTE; - case TelephonyManager.NETWORK_TYPE_NR: - return WifiUsabilityStatsEntry.NETWORK_TYPE_NR; - default: - Log.e(TAG, "Unknown data network type : " + cellularDataNetworkType); - return WifiUsabilityStatsEntry.NETWORK_TYPE_UNKNOWN; - } - } - - private int parseDataNetworkTypeFromProto(int cellularDataNetworkType) { - switch (cellularDataNetworkType) { - case WifiUsabilityStatsEntry.NETWORK_TYPE_UNKNOWN: - return TelephonyManager.NETWORK_TYPE_UNKNOWN; - case WifiUsabilityStatsEntry.NETWORK_TYPE_GSM: - return TelephonyManager.NETWORK_TYPE_GSM; - case WifiUsabilityStatsEntry.NETWORK_TYPE_CDMA: - return TelephonyManager.NETWORK_TYPE_CDMA; - case WifiUsabilityStatsEntry.NETWORK_TYPE_EVDO_0: - return TelephonyManager.NETWORK_TYPE_EVDO_0; - case WifiUsabilityStatsEntry.NETWORK_TYPE_UMTS: - return TelephonyManager.NETWORK_TYPE_UMTS; - case WifiUsabilityStatsEntry.NETWORK_TYPE_TD_SCDMA: - return TelephonyManager.NETWORK_TYPE_TD_SCDMA; - case WifiUsabilityStatsEntry.NETWORK_TYPE_LTE: - return TelephonyManager.NETWORK_TYPE_LTE; - case WifiUsabilityStatsEntry.NETWORK_TYPE_NR: - return TelephonyManager.NETWORK_TYPE_NR; - default: - Log.e(TAG, "Unknown data network type : " + cellularDataNetworkType); - return TelephonyManager.NETWORK_TYPE_UNKNOWN; - } - } /** * Send Wifi usability stats. * @param seqNum @@ -4505,7 +4419,7 @@ public class WifiMetrics { probeStatus = android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_UNKNOWN; Log.e(TAG, "Unknown link probe status: " + s.probeStatusSinceLastUpdate); } - int cellularDataNetworkType = parseDataNetworkTypeFromProto(s.cellularDataNetworkType); + // TODO: remove the following hardcoded values once if they are removed from public API return new android.net.wifi.WifiUsabilityStatsEntry(s.timeStampMs, s.rssi, s.linkSpeedMbps, s.totalTxSuccess, s.totalTxRetries, s.totalTxBad, s.totalRxSuccess, s.totalRadioOnTimeMs, @@ -4514,9 +4428,7 @@ public class WifiMetrics { s.totalPnoScanTimeMs, s.totalHotspot2ScanTimeMs, s.totalCcaBusyFreqTimeMs, s.totalRadioOnFreqTimeMs, s.totalBeaconRx, probeStatus, s.probeElapsedTimeSinceLastUpdateMs, s.probeMcsRateSinceLastUpdate, - s.rxLinkSpeedMbps, cellularDataNetworkType, - s.cellularSignalStrengthDbm, s.cellularSignalStrengthDb, - s.isSameRegisteredCell + s.rxLinkSpeedMbps, 0, 0, 0, false ); } @@ -4551,10 +4463,6 @@ public class WifiMetrics { out.rxLinkSpeedMbps = s.rxLinkSpeedMbps; out.isSameBssidAndFreq = s.isSameBssidAndFreq; out.seqNumInsideFramework = s.seqNumInsideFramework; - out.cellularDataNetworkType = s.cellularDataNetworkType; - out.cellularSignalStrengthDbm = s.cellularSignalStrengthDbm; - out.cellularSignalStrengthDb = s.cellularSignalStrengthDb; - out.isSameRegisteredCell = s.isSameRegisteredCell; out.deviceMobilityState = s.deviceMobilityState; return out; } @@ -5215,18 +5123,18 @@ public class WifiMetrics { /** * Sets the threshold of Tx throughput below which to trigger a data stall */ - public void setDataStallTxTputThrMbps(int txTputThr) { + public void setDataStallTxTputThrKbps(int txTputThr) { synchronized (mLock) { - mExperimentValues.dataStallTxTputThrMbps = txTputThr; + mExperimentValues.dataStallTxTputThrKbps = txTputThr; } } /** * Sets the threshold of Rx throughput below which to trigger a data stall */ - public void setDataStallRxTputThrMbps(int rxTputThr) { + public void setDataStallRxTputThrKbps(int rxTputThr) { synchronized (mLock) { - mExperimentValues.dataStallRxTputThrMbps = rxTputThr; + mExperimentValues.dataStallRxTputThrKbps = rxTputThr; } } diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java index 54b1d6bed7..59d844aed3 100644 --- a/service/java/com/android/server/wifi/WifiMonitor.java +++ b/service/java/com/android/server/wifi/WifiMonitor.java @@ -85,6 +85,8 @@ public class WifiMonitor { /* Indicates assoc reject event */ public static final int ASSOCIATION_REJECTION_EVENT = BASE + 43; public static final int ANQP_DONE_EVENT = BASE + 44; + public static final int ASSOCIATED_BSSID_EVENT = BASE + 45; + public static final int TARGET_BSSID_EVENT = BASE + 46; /* hotspot 2.0 ANQP events */ public static final int GAS_QUERY_START_EVENT = BASE + 51; @@ -473,7 +475,7 @@ public class WifiMonitor { * @param bssid BSSID of the access point. */ public void broadcastAssociatedBssidEvent(String iface, String bssid) { - sendMessage(iface, ClientModeImpl.CMD_ASSOCIATED_BSSID, 0, 0, bssid); + sendMessage(iface, ASSOCIATED_BSSID_EVENT, 0, 0, bssid); } /** @@ -483,7 +485,7 @@ public class WifiMonitor { * @param bssid BSSID of the access point. */ public void broadcastTargetBssidEvent(String iface, String bssid) { - sendMessage(iface, ClientModeImpl.CMD_TARGET_BSSID, 0, 0, bssid); + sendMessage(iface, TARGET_BSSID_EVENT, 0, 0, bssid); } /** diff --git a/service/java/com/android/server/wifi/WifiMulticastLockManager.java b/service/java/com/android/server/wifi/WifiMulticastLockManager.java index f7cd5811b0..ade4dfa8ed 100644 --- a/service/java/com/android/server/wifi/WifiMulticastLockManager.java +++ b/service/java/com/android/server/wifi/WifiMulticastLockManager.java @@ -19,7 +19,7 @@ package com.android.server.wifi; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.util.Slog; +import android.util.Log; import android.util.StatsLog; import com.android.internal.app.IBatteryStats; @@ -75,7 +75,7 @@ public class WifiMulticastLockManager { @Override public void binderDied() { - Slog.e(TAG, "Multicaster binderDied"); + Log.e(TAG, "Multicaster binderDied"); synchronized (mMulticasters) { int i = mMulticasters.indexOf(this); if (i != -1) { diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 3e5dc3c793..b9e7668112 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -24,20 +24,22 @@ import android.net.TrafficStats; import android.net.apf.ApfCapabilities; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; import android.net.wifi.WifiScanner; import android.os.Handler; import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.SystemClock; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Log; -import android.util.SparseArray; import com.android.internal.annotations.Immutable; import com.android.internal.util.HexDump; import com.android.server.net.BaseNetworkObserver; import com.android.server.wifi.util.FrameParser; import com.android.server.wifi.util.NativeUtil; +import com.android.server.wifi.wificond.NativeWifiClient; import java.io.PrintWriter; import java.io.StringWriter; @@ -130,7 +132,7 @@ public class WifiNative { /** Identifier allocated for the interface */ public final int id; /** Type of the iface: STA (for Connectivity or Scan) or AP */ - public final @IfaceType int type; + public @IfaceType int type; /** Name of the interface */ public String name; /** Is the interface up? This is used to mask up/down notifications to external clients. */ @@ -280,6 +282,17 @@ public class WifiNative { return iface.name; } + private @NonNull Set<String> findAllStaIfaceNames() { + Set<String> ifaceNames = new ArraySet<>(); + for (Iface iface : mIfaces.values()) { + if (iface.type == Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY + || iface.type == Iface.IFACE_TYPE_STA_FOR_SCAN) { + ifaceNames.add(iface.name); + } + } + return ifaceNames; + } + /** Removes the existing iface that does not match the provided id. */ public Iface removeExistingIface(int newIfaceId) { Iface removedIface = null; @@ -999,7 +1012,7 @@ public class WifiNative { return null; } iface.externalListener = interfaceCallback; - iface.name = createStaIface(iface, /* lowPrioritySta */ true); + iface.name = createStaIface(iface, /* lowPrioritySta */ false); if (TextUtils.isEmpty(iface.name)) { Log.e(TAG, "Failed to create iface in vendor HAL"); mIfaceMgr.removeIface(iface.id); @@ -1086,6 +1099,79 @@ public class WifiNative { } /** + * Switches an existing Client mode interface from connectivity + * {@link Iface#IFACE_TYPE_STA_FOR_CONNECTIVITY} to scan mode + * {@link Iface#IFACE_TYPE_STA_FOR_SCAN}. + * + * @param ifaceName Name of the interface. + * @return true if the operation succeeded, false if there is an error or the iface is already + * in scan mode. + */ + public boolean switchClientInterfaceToScanMode(@NonNull String ifaceName) { + synchronized (mLock) { + final Iface iface = mIfaceMgr.getIface(ifaceName); + if (iface == null) { + Log.e(TAG, "Trying to switch to scan mode on an invalid iface=" + ifaceName); + return false; + } + if (iface.type == Iface.IFACE_TYPE_STA_FOR_SCAN) { + Log.e(TAG, "Already in scan mode on iface=" + ifaceName); + return true; + } + if (!mSupplicantStaIfaceHal.teardownIface(iface.name)) { + Log.e(TAG, "Failed to teardown iface in supplicant on " + iface); + teardownInterface(iface.name); + return false; + } + iface.type = Iface.IFACE_TYPE_STA_FOR_SCAN; + stopSupplicantIfNecessary(); + iface.featureSet = getSupportedFeatureSetInternal(iface.name); + Log.i(TAG, "Successfully switched to scan mode on iface=" + iface); + return true; + } + } + + /** + * Switches an existing Client mode interface from scan mode + * {@link Iface#IFACE_TYPE_STA_FOR_SCAN} to connectivity mode + * {@link Iface#IFACE_TYPE_STA_FOR_CONNECTIVITY}. + * + * @param ifaceName Name of the interface. + * @return true if the operation succeeded, false if there is an error or the iface is already + * in scan mode. + */ + public boolean switchClientInterfaceToConnectivityMode(@NonNull String ifaceName) { + synchronized (mLock) { + final Iface iface = mIfaceMgr.getIface(ifaceName); + if (iface == null) { + Log.e(TAG, "Trying to switch to connectivity mode on an invalid iface=" + + ifaceName); + return false; + } + if (iface.type == Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY) { + Log.e(TAG, "Already in connectivity mode on iface=" + ifaceName); + return true; + } + if (!startSupplicant()) { + Log.e(TAG, "Failed to start supplicant"); + teardownInterface(iface.name); + mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant(); + return false; + } + if (!mSupplicantStaIfaceHal.setupIface(iface.name)) { + Log.e(TAG, "Failed to setup iface in supplicant on " + iface); + teardownInterface(iface.name); + mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant(); + return false; + } + iface.type = Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY; + iface.featureSet = getSupportedFeatureSetInternal(iface.name); + Log.i(TAG, "Successfully switched to connectivity mode on iface=" + iface); + return true; + } + } + + /** * * Check if the interface is up or down. * @@ -1188,6 +1274,17 @@ public class WifiNative { } /** + * Get names of all the client interfaces. + * + * @return List of interface name of all active client interfaces. + */ + public Set<String> getClientInterfaceNames() { + synchronized (mLock) { + return mIfaceMgr.findAllStaIfaceNames(); + } + } + + /** * Get name of the softap interface. * * This is mainly used by external modules that needs to perform some @@ -1522,9 +1619,9 @@ public class WifiNative { void onFailure(); /** - * Invoked when the number of associated stations changes. + * Invoked when the associated stations changes. */ - void onNumAssociatedStationsChanged(int numStations); + void onConnectedClientsChanged(List<NativeWifiClient> clients); /** * Invoked when the channel switch event happens. @@ -1889,7 +1986,7 @@ public class WifiNative { * @return true if succeeds, false otherwise. */ public boolean simAuthResponse( - @NonNull String ifaceName, int id, String type, String response) { + @NonNull String ifaceName, String type, String response) { if (SIM_AUTH_RESP_TYPE_GSM_AUTH.equals(type)) { return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthResponse( ifaceName, response); @@ -1910,7 +2007,7 @@ public class WifiNative { * @param ifaceName Name of the interface. * @return true if succeeds, false otherwise. */ - public boolean simAuthFailedResponse(@NonNull String ifaceName, int id) { + public boolean simAuthFailedResponse(@NonNull String ifaceName) { return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthFailure(ifaceName); } @@ -1920,7 +2017,7 @@ public class WifiNative { * @param ifaceName Name of the interface. * @return true if succeeds, false otherwise. */ - public boolean umtsAuthFailedResponse(@NonNull String ifaceName, int id) { + public boolean umtsAuthFailedResponse(@NonNull String ifaceName) { return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthFailure(ifaceName); } @@ -1932,8 +2029,8 @@ public class WifiNative { * @param encryptedResponse String to send. * @return true if succeeds, false otherwise. */ - public boolean simIdentityResponse(@NonNull String ifaceName, int id, - String unencryptedResponse, String encryptedResponse) { + public boolean simIdentityResponse(@NonNull String ifaceName, String unencryptedResponse, + String encryptedResponse) { return mSupplicantStaIfaceHal.sendCurrentNetworkEapIdentityResponse(ifaceName, unencryptedResponse, encryptedResponse); } @@ -2089,21 +2186,6 @@ public class WifiNative { } /** - * Migrate all the configured networks from wpa_supplicant. - * - * @param ifaceName Name of the interface. - * @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( - @NonNull String ifaceName, Map<String, WifiConfiguration> configs, - SparseArray<Map<String, String>> networkExtras) { - return mSupplicantStaIfaceHal.loadNetworks(ifaceName, 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. @@ -2216,14 +2298,13 @@ public class WifiNative { return mSupplicantStaIfaceHal.getCurrentNetworkWpsNfcConfigurationToken(ifaceName); } - /** Remove the request |networkId| from supplicant if it's the current network, - * if the current configured network matches |networkId|. + /** + * Clean HAL cached data for |networkId|. * - * @param ifaceName Name of the interface. * @param networkId network id of the network to be removed from supplicant. */ - public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) { - mSupplicantStaIfaceHal.removeNetworkIfCurrent(ifaceName, networkId); + public void removeNetworkCachedData(int networkId) { + mSupplicantStaIfaceHal.removeNetworkCachedData(networkId); } /* @@ -2649,6 +2730,16 @@ public class WifiNative { } /** + * Get the connection Wifi technology + * + * @param ifaceName Name of the interface. + * @return Wifi technology for connection on this interface + */ + public @WifiInfo.WifiTechnology int getWifiTechnology(@NonNull String ifaceName) { + return mSupplicantStaIfaceHal.getWifiTechnology(ifaceName); + } + + /** * Set the MAC OUI during scanning. * An OUI {Organizationally Unique Identifier} is a 24-bit number that * uniquely identifies a vendor or manufacturer. @@ -3261,8 +3352,8 @@ public class WifiNative { ********************************************************/ /* Register native functions */ static { - /* Native functions are defined in libwifi-service.so */ - System.loadLibrary("wifi-service"); + /* Native functions are defined in libwifi-jni.so */ + System.loadLibrary("wifi-jni"); registerNatives(); } diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java index 4b5866b445..bea181ce3f 100644 --- a/service/java/com/android/server/wifi/WifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java @@ -33,19 +33,18 @@ import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkRequest; import android.net.NetworkSpecifier; +import android.net.wifi.IActionListener; import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.INetworkRequestUserSelectionCallback; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.SecurityType; -import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkSpecifier; import android.net.wifi.WifiScanner; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.PatternMatcher; import android.os.Process; import android.os.RemoteException; @@ -121,7 +120,6 @@ public class WifiNetworkFactory extends NetworkFactory { private final PeriodicScanAlarmListener mPeriodicScanTimerListener; private final ConnectionTimeoutAlarmListener mConnectionTimeoutAlarmListener; private final ExternalCallbackTracker<INetworkRequestMatchCallback> mRegisteredCallbacks; - private final Messenger mSrcMessenger; // Store all user approved access points for apps. @VisibleForTesting public final Map<String, LinkedHashSet<AccessPoint>> mUserApprovedAccessPointMap; @@ -299,23 +297,20 @@ public class WifiNetworkFactory extends NetworkFactory { } } - private final Handler.Callback mNetworkConnectionTriggerCallback = (Message msg) -> { - switch (msg.what) { - // Success here means that an attempt to connect to the network has been initiated. - case WifiManager.CONNECT_NETWORK_SUCCEEDED: - if (mVerboseLoggingEnabled) { - Log.v(TAG, "Triggered network connection"); - } - break; - case WifiManager.CONNECT_NETWORK_FAILED: - Log.e(TAG, "Failed to trigger network connection"); - handleNetworkConnectionFailure(mUserSelectedNetwork); - break; - default: - Log.e(TAG, "Unknown message " + msg.what); + private final class ConnectActionListener extends IActionListener.Stub { + @Override + public void onSuccess() { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Triggered network connection"); + } } - return true; - }; + + @Override + public void onFailure(int reason) { + Log.e(TAG, "Failed to trigger network connection"); + handleNetworkConnectionFailure(mUserSelectedNetwork); + } + } /** * Module to interact with the wifi config store. @@ -376,7 +371,6 @@ public class WifiNetworkFactory extends NetworkFactory { mPeriodicScanTimerListener = new PeriodicScanAlarmListener(); mConnectionTimeoutAlarmListener = new ConnectionTimeoutAlarmListener(); mRegisteredCallbacks = new ExternalCallbackTracker<INetworkRequestMatchCallback>(mHandler); - mSrcMessenger = new Messenger(new Handler(looper, mNetworkConnectionTriggerCallback)); mUserApprovedAccessPointMap = new HashMap<>(); // register the data store for serializing/deserializing data. @@ -735,7 +729,8 @@ public class WifiNetworkFactory extends NetworkFactory { // Remove the network if it was added previously by an app's specifier request. if (wcmNetwork.ephemeral && wcmNetwork.fromWifiNetworkSpecifier) { boolean success = - mWifiConfigManager.removeNetwork(wcmNetwork.networkId, wcmNetwork.creatorUid); + mWifiConfigManager.removeNetwork( + wcmNetwork.networkId, wcmNetwork.creatorUid, wcmNetwork.creatorName); if (!success) { Log.e(TAG, "Failed to remove network from config manager"); } else if (mVerboseLoggingEnabled) { @@ -761,11 +756,10 @@ public class WifiNetworkFactory extends NetworkFactory { // Send the connect request to ClientModeImpl. // TODO(b/117601161): Refactor this. - Message msg = Message.obtain(); - msg.what = WifiManager.CONNECT_NETWORK; - msg.arg1 = networkId; - msg.replyTo = mSrcMessenger; - mWifiInjector.getClientModeImpl().sendMessage(msg); + ConnectActionListener connectActionListener = new ConnectActionListener(); + mWifiInjector.getClientModeImpl().connect(null, networkId, new Binder(), + connectActionListener, connectActionListener.hashCode(), + mActiveSpecificNetworkRequestSpecifier.requestorUid); // Post an alarm to handle connection timeout. scheduleConnectionTimeout(); @@ -1102,11 +1096,10 @@ public class WifiNetworkFactory extends NetworkFactory { if (!bssid.matches(matchBaseAddress, matchMask)) { return false; } - if (ScanResultMatchInfo.getNetworkType(wns.wifiConfiguration) - != ScanResultMatchInfo.getNetworkType(scanResult)) { - return false; - } - return true; + ScanResultMatchInfo fromScanResult = ScanResultMatchInfo.fromScanResult(scanResult); + ScanResultMatchInfo fromWifiConfiguration = + ScanResultMatchInfo.fromWifiConfiguration(wns.wifiConfiguration); + return fromScanResult.networkTypeEquals(fromWifiConfiguration); } // Loops through the scan results and finds scan results matching the active network @@ -1167,7 +1160,7 @@ public class WifiNetworkFactory extends NetworkFactory { ApplicationInfo applicationInfo = null; try { applicationInfo = mContext.getPackageManager().getApplicationInfoAsUser( - packageName, 0, UserHandle.getUserId(uid)); + packageName, 0, UserHandle.getUserHandleForUid(uid)); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Failed to find app name for " + packageName); return ""; diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java index 85bd9174a5..0395500143 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java @@ -482,36 +482,6 @@ public class WifiNetworkSelector { } /** - * This returns a list of ScanDetails that were filtered in the process of network selection. - * The list is further filtered for only carrier unsaved networks with EAP encryption. - * - * @param carrierConfig CarrierNetworkConfig used to filter carrier networks - * @return the list of ScanDetails for carrier unsaved networks that do not have invalid SSIDS, - * blacklisted BSSIDS, or low signal strength, and with EAP encryption. This will return an - * empty list when there are no such networks, or when network selection has not been run. - */ - public List<ScanDetail> getFilteredScanDetailsForCarrierUnsavedNetworks( - CarrierNetworkConfig carrierConfig) { - List<ScanDetail> carrierUnsavedNetworks = new ArrayList<>(); - for (ScanDetail scanDetail : mFilteredNetworks) { - ScanResult scanResult = scanDetail.getScanResult(); - - if (!ScanResultUtil.isScanResultForEapNetwork(scanResult) - || !carrierConfig.isCarrierNetwork(scanResult.SSID)) { - continue; - } - - // Skip saved networks - if (mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail) != null) { - continue; - } - - carrierUnsavedNetworks.add(scanDetail); - } - return carrierUnsavedNetworks; - } - - /** * @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 @@ -751,13 +721,12 @@ public class WifiNetworkSelector { if (config != null) { mConnectableNetworks.add(Pair.create(scanDetail, config)); mNetworkIds.add(config.networkId); - if (config.networkId == lastUserSelectedNetworkId) { - wifiCandidates.add(scanDetail, config, - registeredEvaluator.getId(), score, lastSelectionWeight); - } else { - wifiCandidates.add(scanDetail, config, - registeredEvaluator.getId(), score); - } + wifiCandidates.add(scanDetail, config, + registeredEvaluator.getId(), + score, + (config.networkId == lastUserSelectedNetworkId) + ? lastSelectionWeight : 0.0, + WifiConfiguration.isMetered(config, wifiInfo)); mWifiMetrics.setNominatorForNetwork(config.networkId, evaluatorIdToNominatorId(registeredEvaluator.getId())); } @@ -829,7 +798,7 @@ public class WifiNetworkSelector { String chooses = " would choose "; if (candidateScorer == activeScorer) { chooses = " chooses "; - legacyOverrideWanted = candidateScorer.userConnectChoiceOverrideWanted(); + legacyOverrideWanted = choice.userConnectChoiceOverride; selectedNetworkId = networkId; } String id = candidateScorer.getIdentifier(); diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java index 644eb65234..d530cdbfc9 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java +++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java @@ -235,6 +235,9 @@ public class WifiNetworkSuggestionsManager { */ private Set<ExtendedWifiNetworkSuggestion> mActiveNetworkSuggestionsMatchingConnection; + private final Map<String, Set<ExtendedWifiNetworkSuggestion>> + mPasspointInfo = new HashMap<>(); + /** * Intent filter for processing notification actions. */ @@ -307,7 +310,6 @@ public class WifiNetworkSuggestionsManager { } @Override - public void fromDeserialized(Map<String, PerAppInfo> networkSuggestionsMap) { mActiveNetworkSuggestionsPerApp.putAll(networkSuggestionsMap); // Build the scan cache. @@ -320,7 +322,13 @@ public class WifiNetworkSuggestionsManager { startTrackingAppOpsChange(packageName, extNetworkSuggestions.iterator().next().wns.suggestorUid); } - addToScanResultMatchInfoMap(extNetworkSuggestions); + for (ExtendedWifiNetworkSuggestion ewns : extNetworkSuggestions) { + if (ewns.wns.wifiConfiguration.FQDN != null) { + addToPasspointInfoMap(ewns); + } else { + addToScanResultMatchInfoMap(ewns); + } + } } } @@ -329,6 +337,7 @@ public class WifiNetworkSuggestionsManager { mActiveNetworkSuggestionsPerApp.clear(); mActiveScanResultMatchInfoWithBssid.clear(); mActiveScanResultMatchInfoWithNoBssid.clear(); + mPasspointInfo.clear(); } @Override @@ -362,7 +371,7 @@ public class WifiNetworkSuggestionsManager { // Set the user approved flag. setHasUserApprovedForApp(false, packageName); // Take away CHANGE_WIFI_STATE app-ops from the app. - mAppOps.setMode(AppOpsManager.OP_CHANGE_WIFI_STATE, uid, packageName, + mAppOps.setMode(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, uid, packageName, MODE_IGNORED); break; case NOTIFICATION_USER_DISMISSED_INTENT_ACTION: @@ -426,91 +435,118 @@ public class WifiNetworkSuggestionsManager { } private void addToScanResultMatchInfoMap( - @NonNull Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions) { - for (ExtendedWifiNetworkSuggestion extNetworkSuggestion : extNetworkSuggestions) { - ScanResultMatchInfo scanResultMatchInfo = - ScanResultMatchInfo.fromWifiConfiguration( - extNetworkSuggestion.wns.wifiConfiguration); - Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestionsForScanResultMatchInfo; - if (!TextUtils.isEmpty(extNetworkSuggestion.wns.wifiConfiguration.BSSID)) { - Pair<ScanResultMatchInfo, MacAddress> lookupPair = - Pair.create(scanResultMatchInfo, - MacAddress.fromString( - extNetworkSuggestion.wns.wifiConfiguration.BSSID)); - extNetworkSuggestionsForScanResultMatchInfo = - mActiveScanResultMatchInfoWithBssid.get(lookupPair); - if (extNetworkSuggestionsForScanResultMatchInfo == null) { - extNetworkSuggestionsForScanResultMatchInfo = new HashSet<>(); - mActiveScanResultMatchInfoWithBssid.put( - lookupPair, extNetworkSuggestionsForScanResultMatchInfo); - } - } else { - extNetworkSuggestionsForScanResultMatchInfo = - mActiveScanResultMatchInfoWithNoBssid.get(scanResultMatchInfo); - if (extNetworkSuggestionsForScanResultMatchInfo == null) { - extNetworkSuggestionsForScanResultMatchInfo = new HashSet<>(); - mActiveScanResultMatchInfoWithNoBssid.put( - scanResultMatchInfo, extNetworkSuggestionsForScanResultMatchInfo); - } + @NonNull ExtendedWifiNetworkSuggestion extNetworkSuggestion) { + ScanResultMatchInfo scanResultMatchInfo = + ScanResultMatchInfo.fromWifiConfiguration( + extNetworkSuggestion.wns.wifiConfiguration); + Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestionsForScanResultMatchInfo; + if (!TextUtils.isEmpty(extNetworkSuggestion.wns.wifiConfiguration.BSSID)) { + Pair<ScanResultMatchInfo, MacAddress> lookupPair = + Pair.create(scanResultMatchInfo, + MacAddress.fromString( + extNetworkSuggestion.wns.wifiConfiguration.BSSID)); + extNetworkSuggestionsForScanResultMatchInfo = + mActiveScanResultMatchInfoWithBssid.get(lookupPair); + if (extNetworkSuggestionsForScanResultMatchInfo == null) { + extNetworkSuggestionsForScanResultMatchInfo = new HashSet<>(); + mActiveScanResultMatchInfoWithBssid.put( + lookupPair, extNetworkSuggestionsForScanResultMatchInfo); + } + } else { + extNetworkSuggestionsForScanResultMatchInfo = + mActiveScanResultMatchInfoWithNoBssid.get(scanResultMatchInfo); + if (extNetworkSuggestionsForScanResultMatchInfo == null) { + extNetworkSuggestionsForScanResultMatchInfo = new HashSet<>(); + mActiveScanResultMatchInfoWithNoBssid.put( + scanResultMatchInfo, extNetworkSuggestionsForScanResultMatchInfo); } - extNetworkSuggestionsForScanResultMatchInfo.add(extNetworkSuggestion); } + extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); + extNetworkSuggestionsForScanResultMatchInfo.add(extNetworkSuggestion); } private void removeFromScanResultMatchInfoMap( - @NonNull Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions) { - for (ExtendedWifiNetworkSuggestion extNetworkSuggestion : extNetworkSuggestions) { - ScanResultMatchInfo scanResultMatchInfo = - ScanResultMatchInfo.fromWifiConfiguration( - extNetworkSuggestion.wns.wifiConfiguration); - Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestionsForScanResultMatchInfo; - if (!TextUtils.isEmpty(extNetworkSuggestion.wns.wifiConfiguration.BSSID)) { - Pair<ScanResultMatchInfo, MacAddress> lookupPair = - Pair.create(scanResultMatchInfo, - MacAddress.fromString( - extNetworkSuggestion.wns.wifiConfiguration.BSSID)); - extNetworkSuggestionsForScanResultMatchInfo = - mActiveScanResultMatchInfoWithBssid.get(lookupPair); - // This should never happen because we should have done necessary error checks in - // the parent method. - if (extNetworkSuggestionsForScanResultMatchInfo == null) { - Log.wtf(TAG, "No scan result match info found."); - } - extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); - // Remove the set from map if empty. - if (extNetworkSuggestionsForScanResultMatchInfo.isEmpty()) { - mActiveScanResultMatchInfoWithBssid.remove(lookupPair); - } - } else { - extNetworkSuggestionsForScanResultMatchInfo = - mActiveScanResultMatchInfoWithNoBssid.get(scanResultMatchInfo); - // This should never happen because we should have done necessary error checks in - // the parent method. - if (extNetworkSuggestionsForScanResultMatchInfo == null) { - Log.wtf(TAG, "No scan result match info found."); - } - extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); - // Remove the set from map if empty. - if (extNetworkSuggestionsForScanResultMatchInfo.isEmpty()) { - mActiveScanResultMatchInfoWithNoBssid.remove(scanResultMatchInfo); - } + @NonNull ExtendedWifiNetworkSuggestion extNetworkSuggestion) { + ScanResultMatchInfo scanResultMatchInfo = + ScanResultMatchInfo.fromWifiConfiguration( + extNetworkSuggestion.wns.wifiConfiguration); + Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestionsForScanResultMatchInfo; + if (!TextUtils.isEmpty(extNetworkSuggestion.wns.wifiConfiguration.BSSID)) { + Pair<ScanResultMatchInfo, MacAddress> lookupPair = + Pair.create(scanResultMatchInfo, + MacAddress.fromString( + extNetworkSuggestion.wns.wifiConfiguration.BSSID)); + extNetworkSuggestionsForScanResultMatchInfo = + mActiveScanResultMatchInfoWithBssid.get(lookupPair); + // This should never happen because we should have done necessary error checks in + // the parent method. + if (extNetworkSuggestionsForScanResultMatchInfo == null) { + Log.wtf(TAG, "No scan result match info found."); + return; + } + extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); + // Remove the set from map if empty. + if (extNetworkSuggestionsForScanResultMatchInfo.isEmpty()) { + mActiveScanResultMatchInfoWithBssid.remove(lookupPair); } + } else { + extNetworkSuggestionsForScanResultMatchInfo = + mActiveScanResultMatchInfoWithNoBssid.get(scanResultMatchInfo); + // This should never happen because we should have done necessary error checks in + // the parent method. + if (extNetworkSuggestionsForScanResultMatchInfo == null) { + Log.wtf(TAG, "No scan result match info found."); + return; + } + extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); + // Remove the set from map if empty. + if (extNetworkSuggestionsForScanResultMatchInfo.isEmpty()) { + mActiveScanResultMatchInfoWithNoBssid.remove(scanResultMatchInfo); + } + } + } + + private void addToPasspointInfoMap(ExtendedWifiNetworkSuggestion ewns) { + Set<ExtendedWifiNetworkSuggestion> extendedWifiNetworkSuggestions = + mPasspointInfo.get(ewns.wns.wifiConfiguration.FQDN); + if (extendedWifiNetworkSuggestions == null) { + extendedWifiNetworkSuggestions = new HashSet<>(); + } + extendedWifiNetworkSuggestions.add(ewns); + mPasspointInfo.put(ewns.wns.wifiConfiguration.FQDN, extendedWifiNetworkSuggestions); + } + + private void removeFromPassPointInfoMap(ExtendedWifiNetworkSuggestion ewns) { + Set<ExtendedWifiNetworkSuggestion> extendedWifiNetworkSuggestions = + mPasspointInfo.get(ewns.wns.wifiConfiguration.FQDN); + if (extendedWifiNetworkSuggestions == null + || !extendedWifiNetworkSuggestions.contains(ewns)) { + Log.wtf(TAG, "No Passpoint info found."); + return; + } + extendedWifiNetworkSuggestions.remove(ewns); + if (extendedWifiNetworkSuggestions.isEmpty()) { + mPasspointInfo.remove(ewns.wns.wifiConfiguration.FQDN); } } + // Issues a disconnect if the only serving network suggestion is removed. - // TODO (b/115504887): What if there is also a saved network with the same credentials? - private void triggerDisconnectIfServingNetworkSuggestionRemoved( + private void removeFromConfigManagerIfServingNetworkSuggestionRemoved( Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestionsRemoved) { if (mActiveNetworkSuggestionsMatchingConnection == null || mActiveNetworkSuggestionsMatchingConnection.isEmpty()) { return; } + WifiConfiguration activeWifiConfiguration = + mActiveNetworkSuggestionsMatchingConnection.iterator().next().wns.wifiConfiguration; if (mActiveNetworkSuggestionsMatchingConnection.removeAll(extNetworkSuggestionsRemoved)) { if (mActiveNetworkSuggestionsMatchingConnection.isEmpty()) { Log.i(TAG, "Only network suggestion matching the connected network removed. " - + "Disconnecting..."); - mWifiInjector.getClientModeImpl().disconnectCommand(); + + "Removing from config manager..."); + // will trigger a disconnect. + mWifiConfigManager.removeSuggestionConfiguredNetwork( + activeWifiConfiguration.configKey()); } } } @@ -576,29 +612,42 @@ public class WifiNetworkSuggestionsManager { } Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = convertToExtendedWnsSet(networkSuggestions, perAppInfo); - // check if the app is trying to in-place modify network suggestions. - if (!Collections.disjoint(perAppInfo.extNetworkSuggestions, extNetworkSuggestions)) { - Log.e(TAG, "Failed to add network suggestions for " + packageName - + ". Modification of active network suggestions disallowed"); - return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE; - } if (perAppInfo.extNetworkSuggestions.size() + extNetworkSuggestions.size() > WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP) { - Log.e(TAG, "Failed to add network suggestions for " + packageName - + ". Exceeds max per app, current list size: " - + perAppInfo.extNetworkSuggestions.size() - + ", new list size: " - + extNetworkSuggestions.size()); - return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP; + Set<ExtendedWifiNetworkSuggestion> savedNetworkSuggestions = + new HashSet<>(perAppInfo.extNetworkSuggestions); + savedNetworkSuggestions.addAll(extNetworkSuggestions); + if (savedNetworkSuggestions.size() > WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP) { + Log.e(TAG, "Failed to add network suggestions for " + packageName + + ". Exceeds max per app, current list size: " + + perAppInfo.extNetworkSuggestions.size() + + ", new list size: " + + extNetworkSuggestions.size()); + return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP; + } } if (perAppInfo.extNetworkSuggestions.isEmpty()) { // Start tracking app-op changes from the app if they have active suggestions. startTrackingAppOpsChange(packageName, uid); } - perAppInfo.extNetworkSuggestions.addAll(extNetworkSuggestions); + for (ExtendedWifiNetworkSuggestion ewns: extNetworkSuggestions) { + if (ewns.wns.passpointConfiguration == null) { + addToScanResultMatchInfoMap(ewns); + } else { + // Install Passpoint config, if failure, ignore that suggestion + if (!mWifiInjector.getPasspointManager().addOrUpdateProvider( + ewns.wns.passpointConfiguration, uid, + packageName, true)) { + Log.e(TAG, "Passpoint profile install failure."); + continue; + } + addToPasspointInfoMap(ewns); + } + perAppInfo.extNetworkSuggestions.remove(ewns); + perAppInfo.extNetworkSuggestions.add(ewns); + } // Update the max size for this app. perAppInfo.maxSize = Math.max(perAppInfo.extNetworkSuggestions.size(), perAppInfo.maxSize); - addToScanResultMatchInfoMap(extNetworkSuggestions); saveToStore(); mWifiMetrics.incrementNetworkSuggestionApiNumModification(); mWifiMetrics.noteNetworkSuggestionApiListSizeHistogram(getAllMaxSizes()); @@ -615,6 +664,10 @@ public class WifiNetworkSuggestionsManager { mAppOps.stopWatchingMode(appOpsChangedListener); } + /** + * Remove provided list from that App active list. If provided list is empty, will remove all. + * Will disconnect network if current connected network is in the remove list. + */ private void removeInternal( @NonNull Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions, @NonNull String packageName, @@ -634,8 +687,21 @@ public class WifiNetworkSuggestionsManager { // Stop tracking app-op changes from the app if they don't have active suggestions. stopTrackingAppOpsChange(packageName); } - // Clear the scan cache. - removeFromScanResultMatchInfoMap(extNetworkSuggestions); + // Clear the cache. + for (ExtendedWifiNetworkSuggestion ewns : extNetworkSuggestions) { + if (ewns.wns.wifiConfiguration.FQDN != null) { + // Clear the Passpoint config. + mWifiInjector.getPasspointManager().removeProvider( + ewns.wns.suggestorUid, + false, + ewns.wns.wifiConfiguration.FQDN); + removeFromPassPointInfoMap(ewns); + } else { + removeFromScanResultMatchInfoMap(ewns); + } + } + // Disconnect suggested network if connected + removeFromConfigManagerIfServingNetworkSuggestionRemoved(extNetworkSuggestions); } /** @@ -661,13 +727,6 @@ public class WifiNetworkSuggestionsManager { + ". Network suggestions not found in active network suggestions"); return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID; } - if (mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid)) { - // empty list is used to clear everything for the app. - if (extNetworkSuggestions.isEmpty()) { - extNetworkSuggestions = new HashSet<>(perAppInfo.extNetworkSuggestions); - } - triggerDisconnectIfServingNetworkSuggestionRemoved(extNetworkSuggestions); - } removeInternal(extNetworkSuggestions, packageName, perAppInfo); saveToStore(); mWifiMetrics.incrementNetworkSuggestionApiNumModification(); @@ -681,8 +740,6 @@ public class WifiNetworkSuggestionsManager { public void removeApp(@NonNull String packageName) { PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); if (perAppInfo == null) return; - // Disconnect from the current network, if the only suggestion for it was removed. - triggerDisconnectIfServingNetworkSuggestionRemoved(perAppInfo.extNetworkSuggestions); removeInternal(Collections.EMPTY_LIST, packageName, perAppInfo); // Remove the package fully from the internal database. mActiveNetworkSuggestionsPerApp.remove(packageName); @@ -691,14 +748,27 @@ public class WifiNetworkSuggestionsManager { } /** + * Get all network suggestion for target App + * @return List of WifiNetworkSuggestions + */ + public @NonNull List<WifiNetworkSuggestion> get(@NonNull String packageName) { + List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<>(); + PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); + // if App never suggested return empty list. + if (perAppInfo == null) return networkSuggestionList; + for (ExtendedWifiNetworkSuggestion extendedSuggestion : perAppInfo.extNetworkSuggestions) { + networkSuggestionList.add(extendedSuggestion.wns); + } + return networkSuggestionList; + } + + + /** * Clear all internal state (for network settings reset). */ public void clear() { Iterator<Map.Entry<String, PerAppInfo>> iter = mActiveNetworkSuggestionsPerApp.entrySet().iterator(); - // Disconnect if we're connected to one of the suggestions. - triggerDisconnectIfServingNetworkSuggestionRemoved( - mActiveNetworkSuggestionsMatchingConnection); while (iter.hasNext()) { Map.Entry<String, PerAppInfo> entry = iter.next(); removeInternal(Collections.EMPTY_LIST, entry.getKey(), entry.getValue()); @@ -754,7 +824,7 @@ public class WifiNetworkSuggestionsManager { private PendingIntent getPrivateBroadcast(@NonNull String action, @NonNull String packageName, int uid) { Intent intent = new Intent(action) - .setPackage("android") + .setPackage(mWifiInjector.getWifiStackPackageName()) .putExtra(EXTRA_PACKAGE_NAME, packageName) .putExtra(EXTRA_UID, uid); return mFrameworkFacade.getBroadcast(mContext, 0, intent, @@ -765,7 +835,7 @@ public class WifiNetworkSuggestionsManager { ApplicationInfo applicationInfo = null; try { applicationInfo = mContext.getPackageManager().getApplicationInfoAsUser( - packageName, 0, UserHandle.getUserId(uid)); + packageName, 0, UserHandle.getUserHandleForUid(uid)); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Failed to find app name for " + packageName); return ""; @@ -813,15 +883,26 @@ public class WifiNetworkSuggestionsManager { mUserApprovalNotificationPackageName = packageName; } - private boolean sendUserApprovalNotificationIfNotApproved( - @NonNull PerAppInfo perAppInfo, - @NonNull WifiNetworkSuggestion matchingSuggestion) { - if (perAppInfo.hasUserApproved) { + /** + * Send user approval notification if the app is not approved + * @param packageName app package name + * @param uid app UID + * @return true if app is not approved and send notification. + */ + public boolean sendUserApprovalNotificationIfNotApproved( + @NonNull String packageName, @NonNull int uid) { + if (!mActiveNetworkSuggestionsPerApp.containsKey(packageName)) { + Log.wtf(TAG, "AppInfo is missing for " + packageName); + return false; + } + if (mActiveNetworkSuggestionsPerApp.get(packageName).hasUserApproved) { return false; // already approved. } - Log.i(TAG, "Sending user approval notification for " + perAppInfo.packageName); - sendUserApprovalNotification(perAppInfo.packageName, matchingSuggestion.suggestorUid); + Log.i(TAG, "Sending user approval notification for " + packageName); + if (!mUserApprovalNotificationActive) { + sendUserApprovalNotification(packageName, uid); + } return true; } @@ -848,6 +929,14 @@ public class WifiNetworkSuggestionsManager { return extNetworkSuggestions; } + private @Nullable Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForFqdnMatch( + @Nullable String fqdn) { + if (TextUtils.isEmpty(fqdn)) { + return null; + } + return mPasspointInfo.get(fqdn); + } + /** * Returns a set of all network suggestions matching the provided scan detail. */ @@ -882,7 +971,8 @@ public class WifiNetworkSuggestionsManager { && approvedExtNetworkSuggestions.size() != extNetworkSuggestions.size()) { for (ExtendedWifiNetworkSuggestion extNetworkSuggestion : extNetworkSuggestions) { if (sendUserApprovalNotificationIfNotApproved( - extNetworkSuggestion.perAppInfo, extNetworkSuggestion.wns)) { + extNetworkSuggestion.perAppInfo.packageName, + extNetworkSuggestion.wns.suggestorUid)) { break; } } @@ -901,18 +991,22 @@ public class WifiNetworkSuggestionsManager { /** * Returns a set of all network suggestions matching the provided the WifiConfiguration. */ - private @Nullable Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForWifiConfiguration( + public @Nullable Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForWifiConfiguration( @NonNull WifiConfiguration wifiConfiguration, @Nullable String bssid) { Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = null; - try { - ScanResultMatchInfo scanResultMatchInfo = - ScanResultMatchInfo.fromWifiConfiguration(wifiConfiguration); - extNetworkSuggestions = getNetworkSuggestionsForScanResultMatchInfo( - scanResultMatchInfo, bssid == null ? null : MacAddress.fromString(bssid)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Failed to lookup network from scan result match info map", e); + if (wifiConfiguration.isPasspoint()) { + extNetworkSuggestions = getNetworkSuggestionsForFqdnMatch(wifiConfiguration.FQDN); + } else { + try { + ScanResultMatchInfo scanResultMatchInfo = + ScanResultMatchInfo.fromWifiConfiguration(wifiConfiguration); + extNetworkSuggestions = getNetworkSuggestionsForScanResultMatchInfo( + scanResultMatchInfo, bssid == null ? null : MacAddress.fromString(bssid)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to lookup network from scan result match info map", e); + } } - if (extNetworkSuggestions == null) { + if (extNetworkSuggestions == null || extNetworkSuggestions.isEmpty()) { return null; } Set<ExtendedWifiNetworkSuggestion> approvedExtNetworkSuggestions = @@ -926,7 +1020,7 @@ public class WifiNetworkSuggestionsManager { if (mVerboseLoggingEnabled) { Log.v(TAG, "getNetworkSuggestionsFoWifiConfiguration Found " + approvedExtNetworkSuggestions + " for " + wifiConfiguration.SSID - + "[" + wifiConfiguration.allowedKeyManagement + "]"); + + wifiConfiguration.FQDN + "[" + wifiConfiguration.allowedKeyManagement + "]"); } return approvedExtNetworkSuggestions; } @@ -993,7 +1087,8 @@ public class WifiNetworkSuggestionsManager { private void handleConnectionSuccess( @NonNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid) { Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestions = - getNetworkSuggestionsForWifiConfiguration(connectedNetwork, connectedBssid); + getNetworkSuggestionsForWifiConfiguration(connectedNetwork, connectedBssid); + if (mVerboseLoggingEnabled) { Log.v(TAG, "Network suggestions matching the connection " + matchingExtNetworkSuggestions); diff --git a/service/java/com/android/server/wifi/WifiScoreCard.java b/service/java/com/android/server/wifi/WifiScoreCard.java index 973c06dc9f..af995fd62c 100644 --- a/service/java/com/android/server/wifi/WifiScoreCard.java +++ b/service/java/com/android/server/wifi/WifiScoreCard.java @@ -46,6 +46,7 @@ import com.google.protobuf.InvalidProtocolBufferException; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; @@ -67,6 +68,8 @@ public class WifiScoreCard { private static final String TAG = "WifiScoreCard"; private static final boolean DBG = false; + private static final int TARGET_IN_MEMORY_ENTRIES = 50; + private final Clock mClock; private final String mL2KeySeed; private MemoryStore mMemoryStore; @@ -251,6 +254,7 @@ public class WifiScoreCard { if (mValidated) return; // Only once per connection update(Event.VALIDATION_SUCCESS, wifiInfo); mValidated = true; + doWrites(); } /** @@ -369,6 +373,8 @@ public class WifiScoreCard { public final String ssid; public final MacAddress bssid; public boolean changed; + public boolean referenced; + private SecurityType mSecurityType = null; private int mNetworkAgentId = Integer.MIN_VALUE; private int mNetworkConfigId = Integer.MIN_VALUE; @@ -381,6 +387,7 @@ public class WifiScoreCard { this.l2Key = l2KeyFromLong(hash); this.id = idFromLong(hash); this.changed = false; + this.referenced = false; } void updateEventStats(Event event, int frequency, int rssi, int linkspeed) { PerSignal perSignal = lookupSignal(event, frequency); @@ -513,6 +520,8 @@ public class WifiScoreCard { private final PerBssid mDummyPerBssid; private final Map<MacAddress, PerBssid> mApForBssid = new ArrayMap<>(); + private int mApForBssidTargetSize = TARGET_IN_MEMORY_ENTRIES; + private int mApForBssidReferenced = 0; // TODO should be private, but WifiCandidates needs it @NonNull PerBssid lookupBssid(String ssid, String bssid) { @@ -531,9 +540,15 @@ public class WifiScoreCard { PerBssid old = mApForBssid.put(mac, ans); if (old != null) { Log.i(TAG, "Discarding stats for score card (ssid changed) ID: " + old.id); + if (old.referenced) mApForBssidReferenced--; } requestReadForPerBssid(ans); } + if (!ans.referenced) { + ans.referenced = true; + mApForBssidReferenced++; + clean(); + } return ans; } @@ -580,6 +595,34 @@ public class WifiScoreCard { return count; } + /** + * Evicts older entries from memory. + * + * This uses an approximate least-recently-used method. When the number of + * referenced entries exceeds the target value, any items that have not been + * referenced since the last round are evicted, and the remaining entries + * are marked as unreferenced. The total count varies between the target + * value and twice the target value. + */ + private void clean() { + if (mMemoryStore == null) return; + if (mApForBssidReferenced >= mApForBssidTargetSize) { + doWrites(); // Do not want to evict changed items + // Evict the unreferenced ones, and clear all the referenced bits for the next round. + Iterator<Map.Entry<MacAddress, PerBssid>> it = mApForBssid.entrySet().iterator(); + while (it.hasNext()) { + PerBssid perBssid = it.next().getValue(); + if (perBssid.referenced) { + perBssid.referenced = false; + } else { + it.remove(); + if (DBG) Log.v(TAG, "Evict " + perBssid.id); + } + } + mApForBssidReferenced = 0; + } + } + private long computeHashLong(String ssid, MacAddress mac) { byte[][] parts = { // Our seed keeps the L2Keys specific to this device @@ -789,6 +832,7 @@ public class WifiScoreCard { * @param obfuscate - if true, ssids and bssids are omitted (short id only) */ public byte[] getNetworkListByteArray(boolean obfuscate) { + // These are really grouped by ssid, ignoring the security type. Map<String, Network.Builder> networks = new ArrayMap<>(); for (PerBssid perBssid: mApForBssid.values()) { String key = perBssid.ssid; @@ -799,15 +843,12 @@ public class WifiScoreCard { if (!obfuscate) { network.setSsid(perBssid.ssid); } - if (perBssid.mSecurityType != null) { - network.setSecurityType(perBssid.mSecurityType); - } - if (perBssid.mNetworkAgentId >= network.getNetworkAgentId()) { - network.setNetworkAgentId(perBssid.mNetworkAgentId); - } - if (perBssid.mNetworkConfigId >= network.getNetworkConfigId()) { - network.setNetworkConfigId(perBssid.mNetworkConfigId); - } + } + if (perBssid.mNetworkAgentId >= network.getNetworkAgentId()) { + network.setNetworkAgentId(perBssid.mNetworkAgentId); + } + if (perBssid.mNetworkConfigId >= network.getNetworkConfigId()) { + network.setNetworkConfigId(perBssid.mNetworkConfigId); } network.addAccessPoints(perBssid.toAccessPoint(obfuscate)); } diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java index 33cc15058a..63321b3430 100644 --- a/service/java/com/android/server/wifi/WifiScoreReport.java +++ b/service/java/com/android/server/wifi/WifiScoreReport.java @@ -293,7 +293,7 @@ public class WifiScoreReport { synchronized (mLinkMetricsHistory) { history = new LinkedList<>(mLinkMetricsHistory); } - pw.println("time,session,netid,rssi,filtered_rssi,rssi_threshold, freq,txLinkSpeed," + pw.println("time,session,netid,rssi,filtered_rssi,rssi_threshold,freq,txLinkSpeed," + "rxLinkSpeed,tx_good,tx_retry,tx_bad,rx_pps,nudrq,nuds,s1,s2,score"); for (String line : history) { pw.println(line); diff --git a/service/java/com/android/server/wifi/WifiService.java b/service/java/com/android/server/wifi/WifiService.java index b934ea108a..aaf9b19893 100644 --- a/service/java/com/android/server/wifi/WifiService.java +++ b/service/java/com/android/server/wifi/WifiService.java @@ -17,34 +17,30 @@ package com.android.server.wifi; import android.content.Context; +import android.os.Binder; import android.util.Log; -import com.android.server.SystemService; import com.android.server.wifi.util.WifiAsyncChannel; -public final class WifiService extends SystemService { +/** + * Manages the wifi service instance. + */ +public final class WifiService implements WifiServiceBase { private static final String TAG = "WifiService"; final WifiServiceImpl mImpl; public WifiService(Context context) { - super(context); - mImpl = new WifiServiceImpl(context, new WifiInjector(context), new WifiAsyncChannel(TAG)); + mImpl = new WifiServiceImpl(context, WifiInjector.getInstance(), new WifiAsyncChannel(TAG)); } @Override public void onStart() { - Log.i(TAG, "Registering " + Context.WIFI_SERVICE); - publishBinderService(Context.WIFI_SERVICE, mImpl); - } + Log.i(TAG, "Starting " + Context.WIFI_SERVICE); + mImpl.checkAndStartWifi(); - @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - mImpl.checkAndStartWifi(); - } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { - mImpl.handleBootCompleted(); - } + // Trigger all the necessary boot completed actions, since we are starting late now. + mImpl.handleBootCompleted(); } @Override @@ -61,4 +57,9 @@ public final class WifiService extends SystemService { public void onStopUser(int userId) { mImpl.handleUserStop(userId); } + + @Override + public Binder retrieveImpl() { + return mImpl; + } } diff --git a/service/java/com/android/server/wifi/WifiServiceBase.java b/service/java/com/android/server/wifi/WifiServiceBase.java new file mode 100644 index 0000000000..8cf8add6d8 --- /dev/null +++ b/service/java/com/android/server/wifi/WifiServiceBase.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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; + +/** + * Base class for all the wifi services. This is used to manage the lifetime of the services. + * Each service should override the methods corresponding to the lifetime events they care about. + * + * Note: Services can listen to these system broadcasts on their own, but they're explicitly listed + * here to better manage inter-service dependencies. (For ex: wifi aware service needs wifi service + * to initialize the HAL first). + */ +public interface WifiServiceBase { + /** + * Invoked when the APK service is bound. Should bed used to publish + * it's binder service & perform necessary initialization. This should happen very close to + * bootup phase {@link SystemService#PHASE_BOOT_COMPLETED} in system_server. + */ + void onStart(); + + /** + * Invoked when the user switches. + * + * @param userId Id for the new user. + */ + default void onSwitchUser(int userId) {} + + /** + * Invoked when the user unlocks. + * + * @param userId Id for the user. + */ + default void onUnlockUser(int userId) {} + + /** + * Invoked when the user stops. + * + * @param userId Id for the user. + */ + default void onStopUser(int userId) {} + + /** + * Retrieve the underlying binder service implementation. + */ + Binder retrieveImpl(); +} diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 4dbf71fd76..9d3f422cbd 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -18,34 +18,20 @@ package com.android.server.wifi; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -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_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; import static android.net.wifi.WifiManager.WIFI_FEATURE_INFRA_5G; -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_EMERGENCY_CALL_STATE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_SET_AP; -import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; - -import android.Manifest; import android.annotation.CheckResult; -import android.app.ActivityManager; -import android.app.ActivityManager.RunningAppProcessInfo; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppOpsManager; -import android.app.admin.DeviceAdminInfo; -import android.app.admin.DevicePolicyManagerInternal; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.Context; @@ -58,23 +44,31 @@ import android.database.ContentObserver; import android.net.DhcpInfo; import android.net.DhcpResults; import android.net.Network; +import android.net.NetworkStack; import android.net.NetworkUtils; import android.net.Uri; import android.net.ip.IpClientUtil; +import android.net.wifi.IActionListener; import android.net.wifi.IDppCallback; +import android.net.wifi.ILocalOnlyHotspotCallback; import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.IOnWifiUsabilityStatsListener; +import android.net.wifi.IScanResultsListener; import android.net.wifi.ISoftApCallback; import android.net.wifi.ITrafficStateCallback; +import android.net.wifi.ITxPacketCountListener; import android.net.wifi.ScanResult; import android.net.wifi.WifiActivityEnergyInfo; +import android.net.wifi.WifiClient; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.DeviceMobilityState; import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiNetworkSuggestion; +import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; +import android.net.wifi.WifiStackClient; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; @@ -84,11 +78,9 @@ 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; import android.os.Message; -import android.os.Messenger; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -101,8 +93,7 @@ import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; -import android.util.MutableInt; -import android.util.Slog; +import android.util.MutableBoolean; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -111,9 +102,9 @@ 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.wifi.hotspot2.PasspointManager; import com.android.server.wifi.hotspot2.PasspointProvider; import com.android.server.wifi.util.ExternalCallbackTracker; -import com.android.server.wifi.util.GeneralUtil.Mutable; import com.android.server.wifi.util.WifiHandler; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -134,35 +125,31 @@ import java.security.cert.PKIXParameters; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** * WifiService handles remote WiFi operation requests by implementing * the IWifiManager interface. - * - * @hide */ public class WifiServiceImpl extends BaseWifiService { private static final String TAG = "WifiService"; + private static final int APP_INFO_FLAGS_SYSTEM_APP = + ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; private static final boolean VDBG = false; - // 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; - - // Max wait time for posting blocking runnables + /** Max wait time for posting blocking runnables */ private static final int RUN_WITH_SCISSORS_TIMEOUT_MILLIS = 4000; - final ClientModeImpl mClientModeImpl; - final ActiveModeWarden mActiveModeWarden; - final ScanRequestProxy mScanRequestProxy; + private final ClientModeImpl mClientModeImpl; + private final ActiveModeWarden mActiveModeWarden; + private final ScanRequestProxy mScanRequestProxy; private final Context mContext; private final FrameworkFacade mFacade; @@ -171,24 +158,22 @@ public class WifiServiceImpl extends BaseWifiService { private final PowerManager mPowerManager; private final AppOpsManager mAppOps; private final UserManager mUserManager; - private final ActivityManager mActivityManager; private final WifiCountryCode mCountryCode; - // Debug counter tracking scan requests sent by WifiManager - private int scanRequestCounter = 0; - - /* Polls traffic stats and notifies clients */ - private WifiTrafficPoller mWifiTrafficPoller; - /* Tracks the persisted states for wi-fi & airplane mode */ - final WifiSettingsStore mSettingsStore; - /* Logs connection events and some general router and scan stats */ + + /** Polls traffic stats and notifies clients */ + private final WifiTrafficPoller mWifiTrafficPoller; + /** Tracks the persisted states for wi-fi & airplane mode */ + private final WifiSettingsStore mSettingsStore; + /** Logs connection events and some general router and scan stats */ private final WifiMetrics mWifiMetrics; private final WifiInjector mWifiInjector; - /* Backup/Restore Module */ + /** Backup/Restore Module */ private final WifiBackupRestore mWifiBackupRestore; private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; - - private WifiLog mLog; + private final WifiConfigManager mWifiConfigManager; + private final PasspointManager mPasspointManager; + private final WifiLog mLog; /** * Verbose logging flag. Toggled by developer options. */ @@ -202,42 +187,21 @@ public class WifiServiceImpl extends BaseWifiService { 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; - - private final ExternalCallbackTracker<ISoftApCallback> mRegisteredSoftApCallbacks; - - /** - * One of: {@link WifiManager#WIFI_AP_STATE_DISABLED}, - * {@link WifiManager#WIFI_AP_STATE_DISABLING}, - * {@link WifiManager#WIFI_AP_STATE_ENABLED}, - * {@link WifiManager#WIFI_AP_STATE_ENABLING}, - * {@link WifiManager#WIFI_AP_STATE_FAILED} - * - * Access/maintenance MUST be done on the wifi service thread - */ - // TODO: (b/71714381) Remove mWifiApState and broadcast mechanism, keep mSoftApState as the only - // field to store soft AP state. Then rename mSoftApState and mSoftApNumClients to - // mWifiApState and mWifiApNumClients, to match the constants (i.e. WIFI_AP_STATE_*) - private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED; - private int mSoftApState = WifiManager.WIFI_AP_STATE_DISABLED; - private int mSoftApNumClients = 0; + private final WifiPermissionsUtil mWifiPermissionsUtil; /** * Power profile */ - PowerProfile mPowerProfile; + private final PowerProfile mPowerProfile; + + private final TetheredSoftApTracker mTetheredSoftApTracker; + + private final LohsSoftApTracker mLohsSoftApTracker; + + private WifiScanner mWifiScanner; /** * Callback for use with LocalOnlyHotspot to unregister requesting applications upon death. - * - * @hide */ public final class LocalOnlyRequestorCallback implements LocalOnlyHotspotRequestInfo.RequestingApplicationDeathCallback { @@ -246,159 +210,11 @@ public class WifiServiceImpl extends BaseWifiService { */ @Override public void onLocalOnlyHotspotRequestorDeath(LocalOnlyHotspotRequestInfo requestor) { - unregisterCallingAppAndStopLocalOnlyHotspot(requestor); - }; - } - - /** - * Handles client connections - */ - private class AsyncChannelExternalClientHandler extends WifiHandler { - - AsyncChannelExternalClientHandler(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: { - AsyncChannel ac = mFrameworkFacade.makeWifiAsyncChannel(TAG); - ac.connect(mContext, this, msg.replyTo); - break; - } - case WifiManager.CONNECT_NETWORK: { - if (checkPrivilegedPermissionsAndReplyIfNotAuthorized( - msg, WifiManager.CONNECT_NETWORK_FAILED)) { - WifiConfiguration config = (WifiConfiguration) msg.obj; - int networkId = msg.arg1; - Slog.d(TAG, "CONNECT " - + " nid=" + Integer.toString(networkId) - + " config=" + config - + " uid=" + msg.sendingUid - + " name=" - + mContext.getPackageManager().getNameForUid(msg.sendingUid)); - if (config != null) { - /* Command is forwarded to state machine */ - mClientModeImpl.sendMessage(Message.obtain(msg)); - } else if (config == null - && networkId != WifiConfiguration.INVALID_NETWORK_ID) { - mClientModeImpl.sendMessage(Message.obtain(msg)); - } else { - Slog.e(TAG, "AsyncChannelExternalClientHandler.handleMessage " - + "ignoring invalid msg=" + msg); - replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED, - WifiManager.INVALID_ARGS); - } - } - break; - } - case WifiManager.SAVE_NETWORK: { - if (checkPrivilegedPermissionsAndReplyIfNotAuthorized( - msg, WifiManager.SAVE_NETWORK_FAILED)) { - WifiConfiguration config = (WifiConfiguration) msg.obj; - int networkId = msg.arg1; - Slog.d(TAG, "SAVE" - + " nid=" + Integer.toString(networkId) - + " config=" + config - + " uid=" + msg.sendingUid - + " name=" - + mContext.getPackageManager().getNameForUid(msg.sendingUid)); - if (config != null) { - /* Command is forwarded to state machine */ - mClientModeImpl.sendMessage(Message.obtain(msg)); - } else { - Slog.e(TAG, "AsyncChannelExternalClientHandler.handleMessage " - + "ignoring invalid msg=" + msg); - replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.INVALID_ARGS); - } - } - break; - } - case WifiManager.FORGET_NETWORK: - if (checkPrivilegedPermissionsAndReplyIfNotAuthorized( - msg, WifiManager.FORGET_NETWORK_FAILED)) { - mClientModeImpl.sendMessage(Message.obtain(msg)); - } - break; - case WifiManager.DISABLE_NETWORK: - if (checkPrivilegedPermissionsAndReplyIfNotAuthorized( - msg, WifiManager.DISABLE_NETWORK_FAILED)) { - mClientModeImpl.sendMessage(Message.obtain(msg)); - } - break; - case WifiManager.RSSI_PKTCNT_FETCH: { - if (checkChangePermissionAndReplyIfNotAuthorized( - msg, WifiManager.RSSI_PKTCNT_FETCH_FAILED)) { - mClientModeImpl.sendMessage(Message.obtain(msg)); - } - break; - } - default: { - Slog.d(TAG, "AsyncChannelExternalClientHandler.handleMessage " - + "ignoring msg=" + msg); - break; - } - } - } - - /** - * Helper method to check if the sender of the message holds the - * {@link Manifest.permission#CHANGE_WIFI_STATE} permission, and reply with a failure if it - * doesn't - * - * @param msg Incoming message. - * @param replyWhat Param to be filled in the {@link Message#what} field of the failure - * reply. - * @return true if the sender holds the permission, false otherwise. - */ - private boolean checkChangePermissionAndReplyIfNotAuthorized(Message msg, int replyWhat) { - if (!mWifiPermissionsUtil.checkChangePermission(msg.sendingUid)) { - Slog.e(TAG, "AsyncChannelExternalClientHandler.handleMessage " - + "ignoring unauthorized msg=" + msg); - replyFailed(msg, replyWhat, WifiManager.NOT_AUTHORIZED); - return false; - } - return true; - } - - /** - * Helper method to check if the sender of the message holds one of - * {@link Manifest.permission#NETWORK_SETTINGS}, - * {@link Manifest.permission#NETWORK_SETUP_WIZARD} or - * {@link Manifest.permission#NETWORK_STACK} permission, and reply with a failure if it - * doesn't - * - * @param msg Incoming message. - * @param replyWhat Param to be filled in the {@link Message#what} field of the failure - * reply. - * @return true if the sender holds the permission, false otherwise. - */ - private boolean checkPrivilegedPermissionsAndReplyIfNotAuthorized( - Message msg, int replyWhat) { - if (!isPrivileged(-1, msg.sendingUid)) { - Slog.e(TAG, "ClientHandler.handleMessage ignoring unauthorized msg=" + msg); - replyFailed(msg, replyWhat, WifiManager.NOT_AUTHORIZED); - return false; - } - return true; - } - - private void replyFailed(Message msg, int what, int why) { - if (msg.replyTo == null) return; - Message reply = Message.obtain(); - reply.what = what; - reply.arg1 = why; - try { - msg.replyTo.send(reply); - } catch (RemoteException e) { - // There's not much we can do if reply can't be sent! - } + mLog.trace("onLocalOnlyHotspotRequestorDeath pid=%") + .c(requestor.getPid()).flush(); + mLohsSoftApTracker.stopByRequest(requestor); } } - private AsyncChannelExternalClientHandler mAsyncChannelExternalClientHandler; /** * Handles interaction with ClientModeImpl @@ -420,33 +236,32 @@ public class WifiServiceImpl extends BaseWifiService { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { mClientModeImplChannel = mCmiChannel; } else { - Slog.e(TAG, "ClientModeImpl connection failure, error=" + msg.arg1); + Log.e(TAG, "ClientModeImpl connection failure, error=" + msg.arg1); mClientModeImplChannel = null; } break; } case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { - Slog.e(TAG, "ClientModeImpl channel lost, msg.arg1 =" + msg.arg1); + Log.e(TAG, "ClientModeImpl channel lost, msg.arg1 =" + msg.arg1); mClientModeImplChannel = null; //Re-establish connection to state machine mCmiChannel.connect(mContext, this, mClientModeImpl.getHandler()); break; } default: { - Slog.d(TAG, "ClientModeImplHandler.handleMessage ignoring msg=" + msg); + Log.d(TAG, "ClientModeImplHandler.handleMessage ignoring msg=" + msg); break; } } } } - ClientModeImplHandler mClientModeImplHandler; - private WifiController mWifiController; + private final ClientModeImplHandler mClientModeImplHandler; private final WifiLockManager mWifiLockManager; private final WifiMulticastLockManager mWifiMulticastLockManager; private final DppManager mDppManager; - - private WifiApConfigStore mWifiApConfigStore; + private final WifiApConfigStore mWifiApConfigStore; + private final WifiThreadRunner mWifiThreadRunner; public WifiServiceImpl(Context context, WifiInjector wifiInjector, AsyncChannel asyncChannel) { mContext = context; @@ -460,44 +275,31 @@ public class WifiServiceImpl extends BaseWifiService { mCountryCode = mWifiInjector.getWifiCountryCode(); mClientModeImpl = mWifiInjector.getClientModeImpl(); mActiveModeWarden = mWifiInjector.getActiveModeWarden(); - mClientModeImpl.enableRssiPolling(true); + mClientModeImpl.enableRssiPolling(true); //TODO(b/65033024) strange startup mScanRequestProxy = mWifiInjector.getScanRequestProxy(); mSettingsStore = mWifiInjector.getWifiSettingsStore(); mPowerManager = mContext.getSystemService(PowerManager.class); mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); - mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); mWifiLockManager = mWifiInjector.getWifiLockManager(); mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager(); - HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread(); - mAsyncChannelExternalClientHandler = - new AsyncChannelExternalClientHandler(TAG, wifiServiceHandlerThread.getLooper()); mClientModeImplHandler = new ClientModeImplHandler(TAG, - wifiServiceHandlerThread.getLooper(), asyncChannel); - mWifiController = mWifiInjector.getWifiController(); + mWifiInjector.getAsyncChannelHandlerThread().getLooper(), asyncChannel); mWifiBackupRestore = mWifiInjector.getWifiBackupRestore(); mWifiApConfigStore = mWifiInjector.getWifiApConfigStore(); mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil(); mLog = mWifiInjector.makeLog(TAG); mFrameworkFacade = wifiInjector.getFrameworkFacade(); - mIfaceIpModes = new ConcurrentHashMap<>(); - mLocalOnlyHotspotRequests = new HashMap<>(); enableVerboseLoggingInternal(getVerboseLoggingLevel()); - mRegisteredSoftApCallbacks = - new ExternalCallbackTracker<ISoftApCallback>(mClientModeImplHandler); - - mWifiInjector.getActiveModeWarden().registerSoftApCallback(new SoftApCallbackImpl()); + mTetheredSoftApTracker = new TetheredSoftApTracker(); + mActiveModeWarden.registerSoftApCallback(mTetheredSoftApTracker); + mLohsSoftApTracker = new LohsSoftApTracker(); + mActiveModeWarden.registerLohsCallback(mLohsSoftApTracker); mPowerProfile = mWifiInjector.getPowerProfile(); mWifiNetworkSuggestionsManager = mWifiInjector.getWifiNetworkSuggestionsManager(); mDppManager = mWifiInjector.getDppManager(); - } - - /** - * 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) { - mAsyncChannelExternalClientHandler.setWifiLog(log); + mWifiThreadRunner = mWifiInjector.getWifiThreadRunner(); + mWifiConfigManager = mWifiInjector.getWifiConfigManager(); + mPasspointManager = mWifiInjector.getPasspointManager(); } /** @@ -509,16 +311,9 @@ public class WifiServiceImpl extends BaseWifiService { * This function is used only at boot time. */ public void checkAndStartWifi() { - // 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")); + Log.i(TAG, "WifiService starting up with Wi-Fi " + (wifiEnabled ? "enabled" : "disabled")); registerForScanModeChange(); mContext.registerReceiver( @@ -526,7 +321,7 @@ public class WifiServiceImpl extends BaseWifiService { @Override public void onReceive(Context context, Intent intent) { if (mSettingsStore.handleAirplaneModeToggled()) { - mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED); + mActiveModeWarden.airplaneModeToggled(); } } }, @@ -548,25 +343,6 @@ public class WifiServiceImpl extends BaseWifiService { }, 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. @@ -576,33 +352,35 @@ public class WifiServiceImpl extends BaseWifiService { if (!mClientModeImpl.syncInitialize(mClientModeImplChannel)) { Log.wtf(TAG, "Failed to initialize ClientModeImpl"); } - mWifiController.start(); - - // If we are already disabled (could be due to airplane mode), avoid changing persist - // state here - if (wifiEnabled) { - setWifiEnabled(mContext.getPackageName(), wifiEnabled); - } + mActiveModeWarden.start(); } public void handleBootCompleted() { Log.d(TAG, "Handle boot completed"); + mWifiThreadRunner.post(() -> { + new MemoryStoreImpl(mContext, mWifiInjector, mWifiInjector.getWifiScoreCard()).start(); + if (!mWifiConfigManager.loadFromStore()) { + Log.e(TAG, "Failed to load from config store"); + } + mPasspointManager.initializeProvisioner( + mWifiInjector.getPasspointProvisionerHandlerThread().getLooper()); + }); mClientModeImpl.handleBootCompleted(); } public void handleUserSwitch(int userId) { Log.d(TAG, "Handle user switch " + userId); - mClientModeImpl.handleUserSwitch(userId); + mWifiThreadRunner.post(() -> mWifiConfigManager.handleUserSwitch(userId)); } public void handleUserUnlock(int userId) { Log.d(TAG, "Handle user unlock " + userId); - mClientModeImpl.handleUserUnlock(userId); + mWifiThreadRunner.post(() -> mWifiConfigManager.handleUserUnlock(userId)); } public void handleUserStop(int userId) { Log.d(TAG, "Handle user stop " + userId); - mClientModeImpl.handleUserStop(userId); + mWifiThreadRunner.post(() -> mWifiConfigManager.handleUserStop(userId)); } /** @@ -635,22 +413,18 @@ public class WifiServiceImpl extends BaseWifiService { } try { mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid); - Mutable<Boolean> scanSuccess = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler() - .runWithScissors(() -> { - scanSuccess.value = mScanRequestProxy.startScan(callingUid, packageName); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to start scan"); + Boolean scanSuccess = mWifiThreadRunner.call(() -> + mScanRequestProxy.startScan(callingUid, packageName), null); + if (scanSuccess == null) { sendFailedScanBroadcast(); return false; } - if (!scanSuccess.value) { + if (!scanSuccess) { Log.e(TAG, "Failed to start scan"); return false; } } catch (SecurityException e) { - Slog.e(TAG, "Permission violation - startScan not allowed for" + Log.e(TAG, "Permission violation - startScan not allowed for" + " uid=" + callingUid + ", packageName=" + packageName + ", reason=" + e); return false; } finally { @@ -689,10 +463,10 @@ public class WifiServiceImpl extends BaseWifiService { return null; } - boolean mInIdleMode; - boolean mScanPending; + private boolean mInIdleMode; + private boolean mScanPending; - void handleIdleModeChanged() { + private void handleIdleModeChanged() { boolean doScan = false; synchronized (this) { boolean idle = mPowerManager.isDeviceIdleMode(); @@ -729,34 +503,44 @@ public class WifiServiceImpl extends BaseWifiService { == PackageManager.PERMISSION_GRANTED; } + private boolean checkMainlineWifiStackPermission(int pid, int uid) { + return mContext.checkPermission(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK, pid, uid) + == PackageManager.PERMISSION_GRANTED; + } + private boolean checkNetworkManagedProvisioningPermission(int pid, int uid) { return mContext.checkPermission(android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, pid, uid) == PackageManager.PERMISSION_GRANTED; } - // Helper method to check if the entity initiating the binder call has any of the signature only - // permissions. + /** + * Helper method to check if the entity initiating the binder call has any of the signature only + * permissions. + */ private boolean isPrivileged(int pid, int uid) { return checkNetworkSettingsPermission(pid, uid) || checkNetworkSetupWizardPermission(pid, uid) || checkNetworkStackPermission(pid, uid) - || checkNetworkManagedProvisioningPermission(pid, uid); + || checkNetworkManagedProvisioningPermission(pid, uid) + || checkMainlineWifiStackPermission(pid, uid); } - // Helper method to check if the entity initiating the binder call has setup wizard or settings - // permissions. + /** + * Helper method to check if the entity initiating the binder call has setup wizard or settings + * permissions. + */ private boolean isSettingsOrSuw(int pid, int uid) { return checkNetworkSettingsPermission(pid, uid) || checkNetworkSetupWizardPermission(pid, uid); } - // Helper method to check if the entity initiating the binder call is a system app. + /** Helper method to check if the entity initiating the binder call is a system app. */ private boolean isSystem(String packageName, int uid) { long ident = Binder.clearCallingIdentity(); try { ApplicationInfo info = mContext.getPackageManager().getApplicationInfoAsUser( - packageName, 0, UserHandle.getUserId(uid)); - return info.isSystemApp() || info.isUpdatedSystemApp(); + packageName, 0, UserHandle.getUserHandleForUid(uid)); + return (info.flags & APP_INFO_FLAGS_SYSTEM_APP) != 0; } catch (PackageManager.NameNotFoundException e) { // In case of exception, assume unknown app (more strict checking) // Note: This case will never happen since checkPackage is @@ -767,13 +551,10 @@ public class WifiServiceImpl extends BaseWifiService { return false; } - // Helper method to check if the entity initiating the binder call is a DO/PO app. - private boolean isDeviceOrProfileOwner(int uid) { - final DevicePolicyManagerInternal dpmi = - mWifiInjector.getWifiPermissionsWrapper().getDevicePolicyManagerInternal(); - if (dpmi == null) return false; - return dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) - || dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + /** Helper method to check if the entity initiating the binder call is a DO/PO app. */ + private boolean isDeviceOrProfileOwner(int uid, String packageName) { + return mWifiPermissionsUtil.isDeviceOwner(uid, packageName) + || mWifiPermissionsUtil.isProfileOwner(uid, packageName); } private void enforceNetworkSettingsPermission() { @@ -782,8 +563,15 @@ public class WifiServiceImpl extends BaseWifiService { } private void enforceNetworkStackPermission() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK, - "WifiService"); + // TODO(b/142554155): Only check for MAINLINE_NETWORK_STACK permission + boolean granted = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.NETWORK_STACK) + == PackageManager.PERMISSION_GRANTED; + if (granted) { + return; + } + mContext.enforceCallingOrSelfPermission( + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, "WifiService"); } private void enforceAccessPermission() { @@ -812,22 +600,11 @@ public class WifiServiceImpl extends BaseWifiService { AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Binder.getCallingUid(), callingPackage); } - private void enforceLocationHardwarePermission() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, - "LocationHardware"); - } - private void enforceReadCredentialPermission() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_WIFI_CREDENTIAL, "WifiService"); } - private void enforceWorkSourcePermission() { - mContext.enforceCallingPermission(android.Manifest.permission.UPDATE_DEVICE_STATS, - "WifiService"); - - } - private void enforceMulticastChangePermission() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, @@ -853,7 +630,7 @@ public class WifiServiceImpl extends BaseWifiService { return mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q, uid) || isPrivileged(pid, uid) // DO/PO apps should be able to add/modify saved networks. - || isDeviceOrProfileOwner(uid) + || isDeviceOrProfileOwner(uid, packageName) // TODO: Remove this system app bypass once Q is released. || isSystem(packageName, uid) || mWifiPermissionsUtil.checkSystemAlertWindowPermission(uid, packageName); @@ -871,7 +648,7 @@ public class WifiServiceImpl extends BaseWifiService { return false; } boolean isPrivileged = isPrivileged(Binder.getCallingPid(), Binder.getCallingUid()); - if (!isPrivileged && !isDeviceOrProfileOwner(Binder.getCallingUid()) + if (!isPrivileged && !isDeviceOrProfileOwner(Binder.getCallingUid(), packageName) && !mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q, Binder.getCallingUid()) && !isSystem(packageName, Binder.getCallingUid())) { @@ -886,14 +663,8 @@ public class WifiServiceImpl extends BaseWifiService { } // If SoftAp is enabled, only privileged apps are allowed to toggle wifi - boolean apEnabled = mWifiApState == WifiManager.WIFI_AP_STATE_ENABLED; - if (apEnabled && !isPrivileged) { - mLog.err("setWifiEnabled SoftAp enabled: only Settings can toggle wifi").flush(); - return false; - } - - // If we're in crypt debounce, ignore any wifi state change APIs. - if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) { + if (!isPrivileged && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) { + mLog.err("setWifiEnabled with SoftAp enabled: only Settings can toggle wifi").flush(); return false; } @@ -909,7 +680,7 @@ public class WifiServiceImpl extends BaseWifiService { Binder.restoreCallingIdentity(ident); } mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable); - mWifiController.sendMessage(CMD_WIFI_TOGGLED); + mActiveModeWarden.wifiToggled(); return true; } @@ -944,13 +715,7 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getWifiApEnabledState uid=%").c(Binder.getCallingUid()).flush(); } - - // hand off work to our handler thread - MutableInt apState = new MutableInt(WifiManager.WIFI_AP_STATE_DISABLED); - mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - apState.value = mWifiApState; - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - return apState.value; + return mTetheredSoftApTracker.getState(); } /** @@ -972,71 +737,7 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("updateInterfaceIpState uid=%").c(Binder.getCallingUid()).flush(); // hand off the work to our handler thread - mWifiInjector.getClientModeImplHandler().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: - if (!isConcurrentLohsAndTetheringSupported()) { - /* 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: - Slog.d(TAG, "IP mode config error - need to clean up"); - if (mLocalOnlyHotspotRequests.isEmpty()) { - Slog.d(TAG, "no LOHS requests, stop softap"); - stopSoftAp(); - } else { - Slog.d(TAG, "we have LOHS requests, clean them up"); - // 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.warn("updateInterfaceIpStateInternal: unknown mode %").c(mode).flush(); - } - } + mWifiThreadRunner.post(() -> mLohsSoftApTracker.updateInterfaceIpState(ifaceName, mode)); } /** @@ -1049,27 +750,18 @@ public class WifiServiceImpl extends BaseWifiService { public boolean startSoftAp(WifiConfiguration wifiConfig) { // NETWORK_STACK is a signature only permission. enforceNetworkStackPermission(); - // If we're in crypt debounce, ignore any wifi state change APIs. - if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) { - return false; - } mLog.info("startSoftAp uid=%").c(Binder.getCallingUid()).flush(); - synchronized (mLocalOnlyHotspotRequests) { - // If a tethering request comes in while we have an existing tethering session, return - // error. - if (mIfaceIpModes.contains(WifiManager.IFACE_IP_MODE_TETHERED)) { - mLog.err("Tethering is already active.").flush(); - return false; - } - // 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 (!isConcurrentLohsAndTetheringSupported() && !mLocalOnlyHotspotRequests.isEmpty()) { - stopSoftApInternal(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + if (mTetheredSoftApTracker.setEnablingIfAllowed()) { + if (!isConcurrentLohsAndTetheringSupported()) { + // Take down LOHS if it is up. + mLohsSoftApTracker.stopAll(); } return startSoftApInternal(wifiConfig, WifiManager.IFACE_IP_MODE_TETHERED); } + mLog.err("Tethering is already active.").flush(); + return false; } /** @@ -1083,10 +775,10 @@ public class WifiServiceImpl extends BaseWifiService { // null wifiConfig is a meaningful input for CMD_SET_AP if (wifiConfig == null || WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) { SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig); - mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig); + mActiveModeWarden.startSoftAp(softApConfig); return true; } - Slog.e(TAG, "Invalid WifiConfiguration"); + Log.e(TAG, "Invalid WifiConfiguration"); return false; } @@ -1099,46 +791,80 @@ public class WifiServiceImpl extends BaseWifiService { public boolean stopSoftAp() { // NETWORK_STACK is a signature only permission. enforceNetworkStackPermission(); - // If we're in crypt debounce, ignore any wifi state change APIs. - if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) { - return false; - } // 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.info("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()) { - // This shouldn't affect devices that support concurrent LOHS and tethering - mLog.trace("Call to stop Tethering while LOHS is active," - + " Registered LOHS callers will be updated when softap stopped.").flush(); - } - - return stopSoftApInternal(WifiManager.IFACE_IP_MODE_TETHERED); - } + stopSoftApInternal(WifiManager.IFACE_IP_MODE_TETHERED); + return true; } /** - * Internal method to stop softap mode. Callers of this method should have already checked + * Internal method to stop softap mode. + * + * Callers of this method should have already checked * proper permissions beyond the NetworkStack permission. + * + * @param mode the operating mode of APs to bring down (ex, + * {@link WifiManager.IFACE_IP_MODE_TETHERED} or + * {@link WifiManager.IFACE_IP_MODE_LOCAL_ONLY}). + * Use {@link WifiManager.IFACE_IP_MODE_UNSPECIFIED} to stop all APs. */ - private boolean stopSoftApInternal(int mode) { - mLog.trace("stopSoftApInternal uid=%").c(Binder.getCallingUid()).flush(); + private void stopSoftApInternal(int mode) { + mLog.trace("stopSoftApInternal uid=% mode=%").c(Binder.getCallingUid()).c(mode).flush(); - mWifiController.sendMessage(CMD_SET_AP, 0, mode); - return true; + mActiveModeWarden.stopSoftAp(mode); } /** - * Callback to use with ClientModeImpl to receive events from ClientModeImpl - * - * @hide + * SoftAp callback */ - private final class SoftApCallbackImpl implements WifiManager.SoftApCallback { + private final class TetheredSoftApTracker implements WifiManager.SoftApCallback { + /** + * State of tethered SoftAP + * One of: {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + */ + private final Object mLock = new Object(); + private int mTetheredSoftApState = WIFI_AP_STATE_DISABLED; + private List<WifiClient> mTetheredSoftApConnectedClients = new ArrayList<>(); + + public int getState() { + synchronized (mLock) { + return mTetheredSoftApState; + } + } + + public boolean setEnablingIfAllowed() { + synchronized (mLock) { + if (mTetheredSoftApState == WIFI_AP_STATE_ENABLING) return false; + if (mTetheredSoftApState == WIFI_AP_STATE_ENABLED) return false; + mTetheredSoftApState = WIFI_AP_STATE_ENABLING; + return true; + } + } + + public List<WifiClient> getConnectedClients() { + return mTetheredSoftApConnectedClients; + } + + private final ExternalCallbackTracker<ISoftApCallback> mRegisteredSoftApCallbacks = + new ExternalCallbackTracker<>(mClientModeImplHandler); + + public boolean registerSoftApCallback(IBinder binder, ISoftApCallback callback, + int callbackIdentifier) { + return mRegisteredSoftApCallbacks.add(binder, callback, callbackIdentifier); + } + + public void unregisterSoftApCallback(int callbackIdentifier) { + mRegisteredSoftApCallbacks.remove(callbackIdentifier); + } + /** * Called when soft AP state changes. * @@ -1150,7 +876,9 @@ public class WifiServiceImpl extends BaseWifiService { */ @Override public void onStateChanged(int state, int failureReason) { - mSoftApState = state; + synchronized (mLock) { + mTetheredSoftApState = state; + } Iterator<ISoftApCallback> iterator = mRegisteredSoftApCallbacks.getCallbacks().iterator(); @@ -1160,28 +888,30 @@ public class WifiServiceImpl extends BaseWifiService { callback.onStateChanged(state, failureReason); } catch (RemoteException e) { Log.e(TAG, "onStateChanged: remote exception -- " + e); + // TODO(b/138863863) remove does nothing, getCallbacks() returns a copy iterator.remove(); } } } /** - * Called when number of connected clients to soft AP changes. + * Called when the connected clients to soft AP changes. * - * @param numClients number of connected clients to soft AP + * @param clients connected clients to soft AP */ @Override - public void onNumClientsChanged(int numClients) { - mSoftApNumClients = numClients; + public void onConnectedClientsChanged(List<WifiClient> clients) { + mTetheredSoftApConnectedClients = new ArrayList<>(clients); Iterator<ISoftApCallback> iterator = mRegisteredSoftApCallbacks.getCallbacks().iterator(); while (iterator.hasNext()) { ISoftApCallback callback = iterator.next(); try { - callback.onNumClientsChanged(numClients); + callback.onConnectedClientsChanged(mTetheredSoftApConnectedClients); } catch (RemoteException e) { - Log.e(TAG, "onNumClientsChanged: remote exception -- " + e); + Log.e(TAG, "onConnectedClientsChanged: remote exception -- " + e); + // TODO(b/138863863) remove does nothing, getCallbacks() returns a copy iterator.remove(); } } @@ -1189,6 +919,306 @@ public class WifiServiceImpl extends BaseWifiService { } /** + * Lohs callback + */ + private final class LohsSoftApTracker implements WifiManager.SoftApCallback { + @GuardedBy("mLocalOnlyHotspotRequests") + private final HashMap<Integer, LocalOnlyHotspotRequestInfo> + mLocalOnlyHotspotRequests = new HashMap<>(); + + @GuardedBy("mLocalOnlyHotspotRequests") + private WifiConfiguration mLocalOnlyHotspotConfig = null; + + @GuardedBy("mLocalOnlyHotspotRequests") + private String mLohsInterfaceName; + + /** + * State of local-only hotspot + * One of: {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + */ + @GuardedBy("mLocalOnlyHotspotRequests") + private int mLohsState = WIFI_AP_STATE_DISABLED; + + @GuardedBy("mLocalOnlyHotspotRequests") + private int mLohsInterfaceMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED; + + public void updateInterfaceIpState(String ifaceName, int mode) { + // update interface IP state related to local-only hotspot + synchronized (mLocalOnlyHotspotRequests) { + Log.d(TAG, "updateInterfaceIpState: ifaceName=" + ifaceName + " mode=" + mode + + " previous LOHS mode= " + mLohsInterfaceMode); + + switch (mode) { + case WifiManager.IFACE_IP_MODE_LOCAL_ONLY: + // first make sure we have registered requests. + if (mLocalOnlyHotspotRequests.isEmpty()) { + // we don't have requests... stop the hotspot + Log.wtf(TAG, "Starting LOHS without any requests?"); + stopSoftApInternal(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + return; + } + // LOHS is ready to go! Call our registered requestors! + mLohsInterfaceName = ifaceName; + mLohsInterfaceMode = mode; + sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked(); + break; + case WifiManager.IFACE_IP_MODE_TETHERED: + if (mLohsInterfaceName != null + && mLohsInterfaceName.equals(ifaceName)) { + /* This shouldn't happen except in a race, but if it does, tear down + * the LOHS and let tethering win. + * + * If concurrent SAPs are allowed, the interface names will differ, + * so we don't have to check the config here. + */ + Log.e(TAG, "Unexpected IP mode change on " + ifaceName); + mLohsInterfaceName = null; + mLohsInterfaceMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED; + sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( + LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE); + } + break; + case WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR: + if (ifaceName == null) { + // All softAps + mLohsInterfaceName = null; + mLohsInterfaceMode = mode; + sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( + LocalOnlyHotspotCallback.ERROR_GENERIC); + stopSoftApInternal(WifiManager.IFACE_IP_MODE_UNSPECIFIED); + } else if (ifaceName.equals(mLohsInterfaceName)) { + mLohsInterfaceName = null; + mLohsInterfaceMode = mode; + sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( + LocalOnlyHotspotCallback.ERROR_GENERIC); + stopSoftApInternal(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + } else { + // Not for LOHS. This is the wrong place to do this, but... + stopSoftApInternal(WifiManager.IFACE_IP_MODE_TETHERED); + } + break; + case WifiManager.IFACE_IP_MODE_UNSPECIFIED: + if (ifaceName == null || ifaceName.equals(mLohsInterfaceName)) { + mLohsInterfaceName = null; + mLohsInterfaceMode = mode; + } + break; + default: + mLog.warn("updateInterfaceIpState: unknown mode %").c(mode).flush(); + } + } + } + + /** + * Helper method to send a HOTSPOT_FAILED message to all registered LocalOnlyHotspotRequest + * callers and clear the registrations. + * + * Callers should already hold the mLocalOnlyHotspotRequests lock. + */ + @GuardedBy("mLocalOnlyHotspotRequests") + private void sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(int reason) { + for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) { + try { + requestor.sendHotspotFailedMessage(reason); + 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. + */ + @GuardedBy("mLocalOnlyHotspotRequests") + 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(); + } + + /** + * Add a new LOHS client + */ + private int start(int pid, LocalOnlyHotspotRequestInfo request) { + synchronized (mLocalOnlyHotspotRequests) { + // does this caller already have a request? + if (mLocalOnlyHotspotRequests.get(pid) != null) { + mLog.trace("caller already has an active request").flush(); + throw new IllegalStateException( + "Caller already has an active LocalOnlyHotspot request"); + } + + // check current operating state and take action if needed + if (mLohsInterfaceMode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY) { + // LOHS is already active, send out what is running + try { + mLog.trace("LOHS already up, trigger onStarted callback").flush(); + 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 + boolean is5Ghz = hasAutomotiveFeature(mContext) + && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_wifi_local_only_hotspot_5ghz) + && is5GhzSupported(); + + mLocalOnlyHotspotConfig = + WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext, + is5Ghz ? WifiConfiguration.AP_BAND_5GHZ + : WifiConfiguration.AP_BAND_2GHZ); + + startSoftApInternal(mLocalOnlyHotspotConfig, + WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + } + + mLocalOnlyHotspotRequests.put(pid, request); + return LocalOnlyHotspotCallback.REQUEST_REGISTERED; + } + } + + /** + * Requests that any local-only hotspot be stopped. + */ + public void stopAll() { + synchronized (mLocalOnlyHotspotRequests) { + if (!mLocalOnlyHotspotRequests.isEmpty()) { + // This is used to take down LOHS when tethering starts, and in that + // case we send failed instead of stopped. + // TODO check if that is right. Calling onFailed instead of onStopped when the + // hotspot is already started does not seem to match the documentation + sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( + LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE); + stopIfEmptyLocked(); + } + } + } + + /** + * Unregisters the LOHS request from the given process and stops LOHS if no other clients. + */ + public void stopByPid(int pid) { + synchronized (mLocalOnlyHotspotRequests) { + LocalOnlyHotspotRequestInfo requestInfo = mLocalOnlyHotspotRequests.remove(pid); + if (requestInfo == null) return; + requestInfo.unlinkDeathRecipient(); + stopIfEmptyLocked(); + } + } + + /** + * Unregisters LocalOnlyHotspot request and stops the hotspot if needed. + */ + public void stopByRequest(LocalOnlyHotspotRequestInfo request) { + + synchronized (mLocalOnlyHotspotRequests) { + if (mLocalOnlyHotspotRequests.remove(request.getPid()) == null) { + mLog.trace("LocalOnlyHotspotRequestInfo not found to remove").flush(); + return; + } + stopIfEmptyLocked(); + } + } + + @GuardedBy("mLocalOnlyHotspotRequests") + private void stopIfEmptyLocked() { + if (mLocalOnlyHotspotRequests.isEmpty()) { + mLocalOnlyHotspotConfig = null; + mLohsInterfaceName = null; + mLohsInterfaceMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED; + stopSoftApInternal(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + } + } + + + /** + * Helper method to send a HOTSPOT_STARTED message to all registered LocalOnlyHotspotRequest + * callers. + * + * Callers should already hold the mLocalOnlyHotspotRequests lock. + */ + @GuardedBy("mLocalOnlyHotspotRequests") + private void sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked() { + for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) { + try { + requestor.sendHotspotStartedMessage(mLocalOnlyHotspotConfig); + } catch (RemoteException e) { + // This will be cleaned up by binder death handling + } + } + } + + @Override + public void onStateChanged(int state, int failureReason) { + // The AP state update from ClientModeImpl for softap + synchronized (mLocalOnlyHotspotRequests) { + Log.d(TAG, "lohs.onStateChanged: currentState=" + state + + " previousState=" + mLohsState + " errorCode= " + failureReason + + " ifaceName=" + mLohsInterfaceName); + + // check if we have a failure - since it is possible (worst case scenario where + // WifiController and ClientModeImpl are out of sync wrt modes) to get two FAILED + // notifications in a row, we need to handle this first. + if (state == WIFI_AP_STATE_FAILED) { + // update registered LOHS callbacks if we see a failure + int errorToReport = ERROR_GENERIC; + if (failureReason == 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 + updateInterfaceIpState(mLohsInterfaceName, + WifiManager.IFACE_IP_MODE_UNSPECIFIED); + } else if (state == WIFI_AP_STATE_DISABLING || state == WIFI_AP_STATE_DISABLED) { + // softap is shutting down or is down... let requestors know via the + // onStopped call + // if we are currently in hotspot mode, then trigger onStopped for registered + // requestors, otherwise something odd happened and we should clear state + if (mLohsInterfaceName != null + && mLohsInterfaceMode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY) { + // holding the required lock: send message to requestors and clear the list + sendHotspotStoppedMessageToAllLOHSRequestInfoEntriesLocked(); + } else if (!isConcurrentLohsAndTetheringSupported()) { + // LOHS not active: report an error (still holding the required lock) + sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(ERROR_GENERIC); + } + // also clear interface ip state + updateInterfaceIpState(mLohsInterfaceName, + WifiManager.IFACE_IP_MODE_UNSPECIFIED); + } + // For enabling and enabled, just record the new state + mLohsState = state; + } + } + + @Override + public void onConnectedClientsChanged(List<WifiClient> clients) { + // Nothing to do + } + } + + /** * see {@link android.net.wifi.WifiManager#registerSoftApCallback(SoftApCallback, Handler)} * * @param binder IBinder instance to allow cleanup if the app dies @@ -1217,19 +1247,19 @@ public class WifiServiceImpl extends BaseWifiService { } // post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - if (!mRegisteredSoftApCallbacks.add(binder, callback, callbackIdentifier)) { + mWifiThreadRunner.post(() -> { + if (!mTetheredSoftApTracker.registerSoftApCallback(binder, callback, + callbackIdentifier)) { Log.e(TAG, "registerSoftApCallback: Failed to add callback"); return; } // Update the client about the current state immediately after registering the callback try { - callback.onStateChanged(mSoftApState, 0); - callback.onNumClientsChanged(mSoftApNumClients); + callback.onStateChanged(mTetheredSoftApTracker.getState(), 0); + callback.onConnectedClientsChanged(mTetheredSoftApTracker.getConnectedClients()); } catch (RemoteException e) { Log.e(TAG, "registerSoftApCallback: remote exception -- " + e); } - }); } @@ -1242,143 +1272,24 @@ public class WifiServiceImpl extends BaseWifiService { */ @Override public void unregisterSoftApCallback(int callbackIdentifier) { - enforceNetworkSettingsPermission(); if (mVerboseLoggingEnabled) { mLog.info("unregisterSoftApCallback uid=%").c(Binder.getCallingUid()).flush(); } // post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mRegisteredSoftApCallbacks.remove(callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mTetheredSoftApTracker.unregisterSoftApCallback(callbackIdentifier)); } /** - * Private method to handle SoftAp state changes - * - * <p> MUST be called from the ClientModeImpl thread. - */ - private void handleWifiApStateChange( - int currentState, int previousState, int errorCode, String ifaceName, int mode) { - // The AP state update from ClientModeImpl for softap - Slog.d(TAG, "handleWifiApStateChange: currentState=" + currentState - + " previousState=" + previousState + " errorCode= " + errorCode - + " ifaceName=" + ifaceName + " mode=" + mode); - - // update the tracking ap state variable - mWifiApState = currentState; - - // check if we have a failure - since it is possible (worst case scenario where - // WifiController and ClientModeImpl 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.getOrDefault(ifaceName, WifiManager.IFACE_IP_MODE_UNSPECIFIED) - == WifiManager.IFACE_IP_MODE_LOCAL_ONLY) { - // holding the required lock: send message to requestors and clear the list - sendHotspotStoppedMessageToAllLOHSRequestInfoEntriesLocked(); - } else if (!isConcurrentLohsAndTetheringSupported()) { - // 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. - */ - @GuardedBy("mLocalOnlyHotspotRequests") - 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. - */ - @GuardedBy("mLocalOnlyHotspotRequests") - 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. - */ - @GuardedBy("mLocalOnlyHotspotRequests") - 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 + * Temporary method used for testing while start 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); + mLohsSoftApTracker.start(pid, request); } /** @@ -1391,8 +1302,7 @@ public class WifiServiceImpl extends BaseWifiService { * * 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 callback Callback to communicate with WifiManager and allow cleanup if the app dies. * @param packageName String name of the calling package * * @return int return code for attempt to start LocalOnlyHotspot. @@ -1403,24 +1313,21 @@ public class WifiServiceImpl extends BaseWifiService { * have an outstanding request. */ @Override - public int startLocalOnlyHotspot(Messenger messenger, IBinder binder, String packageName) { + public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, 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(); + mLog.info("start uid=% pid=%").c(uid).c(pid).flush(); + if (enforceChangePermission(packageName) != MODE_ALLOWED) { return LocalOnlyHotspotCallback.ERROR_GENERIC; } enforceLocationPermission(packageName, uid); - long ident = Binder.clearCallingIdentity(); - try { - // also need to verify that Locations services are enabled. - if (!mWifiPermissionsUtil.isLocationModeEnabled()) { - throw new SecurityException("Location mode is not enabled."); - } - } finally { - Binder.restoreCallingIdentity(ident); + // also need to verify that Locations services are enabled. + if (!Binder.withCleanCallingIdentity(() -> mWifiPermissionsUtil.isLocationModeEnabled())) { + throw new SecurityException("Location mode is not enabled."); } // verify that tethering is not disabled @@ -1429,63 +1336,26 @@ public class WifiServiceImpl extends BaseWifiService { } // the app should be in the foreground - if (!mFrameworkFacade.isAppForeground(uid)) { + if (!Binder.withCleanCallingIdentity( + () -> mFrameworkFacade.isAppForeground(mContext, uid))) { return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; } - if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) { + // check if we are currently tethering + // TODO(b/123227116): handle all interface combinations just by changing the HAL. + if (!isConcurrentLohsAndTetheringSupported() + && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) { + // Tethering is enabled, cannot start LocalOnlyHotspot + mLog.info("Cannot start localOnlyHotspot when WiFi Tethering is active.") + .flush(); return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; } - mLog.info("startLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); - - synchronized (mLocalOnlyHotspotRequests) { - // check if we are currently tethering - // TODO(b/123227116): handle all interface combinations just by changing the HAL. - if (!isConcurrentLohsAndTetheringSupported() - && mIfaceIpModes.contains(WifiManager.IFACE_IP_MODE_TETHERED)) { - // Tethering is enabled, cannot start LocalOnlyHotspot - mLog.info("Cannot start localOnlyHotspot when WiFi Tethering is active.").flush(); - 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").flush(); - throw new IllegalStateException( - "Caller already has an active LocalOnlyHotspot request"); - } + // now create the new LOHS request info object + LocalOnlyHotspotRequestInfo request = new LocalOnlyHotspotRequestInfo(callback, + new LocalOnlyRequestorCallback()); - // 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").flush(); - 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 - boolean is5Ghz = hasAutomotiveFeature(mContext) - && mContext.getResources().getBoolean( - com.android.internal.R.bool.config_wifi_local_only_hotspot_5ghz) - && is5GhzSupported(); - - mLocalOnlyHotspotConfig = WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext, - is5Ghz ? WifiConfiguration.AP_BAND_5GHZ : WifiConfiguration.AP_BAND_2GHZ); - - startSoftApInternal(mLocalOnlyHotspotConfig, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - } - - mLocalOnlyHotspotRequests.put(pid, request); - return LocalOnlyHotspotCallback.REQUEST_REGISTERED; - } + return mLohsSoftApTracker.start(pid, request); } /** @@ -1505,41 +1375,7 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("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").flush(); - 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(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } + mLohsSoftApTracker.stopByPid(pid); } /** @@ -1547,8 +1383,7 @@ public class WifiServiceImpl extends BaseWifiService { * * 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 + * @param callback Callback to communicate with WifiManager and allow cleanup if the app dies. * * @throws SecurityException if the caller does not have permission to watch Local Only Hotspot * status updates. @@ -1556,7 +1391,7 @@ public class WifiServiceImpl extends BaseWifiService { * an existing subscription. */ @Override - public void startWatchLocalOnlyHotspot(Messenger messenger, IBinder binder) { + public void startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback callback) { // NETWORK_SETTINGS is a signature only permission. enforceNetworkSettingsPermission(); @@ -1589,19 +1424,15 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException("App not allowed to read or update stored WiFi Ap config " + "(uid = " + uid + ")"); } - mLog.info("getWifiApConfiguration uid=%").c(uid).flush(); + + if (mVerboseLoggingEnabled) { + mLog.info("getWifiApConfiguration uid=%").c(uid).flush(); + } // hand off work to the ClientModeImpl handler thread to sync work between calls // and SoftApManager starting up softap - final Mutable<WifiConfiguration> config = new Mutable(); - boolean success = mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - config.value = mWifiApConfigStore.getApConfiguration(); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (success) { - return config.value; - } - Log.e(TAG, "Failed to post runnable to fetch ap config"); - return new WifiConfiguration(); + return mWifiThreadRunner.call(mWifiApConfigStore::getApConfiguration, + new WifiConfiguration()); } /** @@ -1626,12 +1457,10 @@ public class WifiServiceImpl extends BaseWifiService { if (wifiConfig == null) return false; if (WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) { - mClientModeImplHandler.post(() -> { - mWifiApConfigStore.setApConfiguration(wifiConfig); - }); + mWifiThreadRunner.post(() -> mWifiApConfigStore.setApConfiguration(wifiConfig)); return true; } else { - Slog.e(TAG, "Invalid WifiConfiguration"); + Log.e(TAG, "Invalid WifiConfiguration"); return false; } } @@ -1784,19 +1613,18 @@ public class WifiServiceImpl extends BaseWifiService { rxIdleTime * rxIdleCurrent) * voltage); if (VDBG || rxIdleTime < 0 || stats.on_time < 0 || stats.tx_time < 0 || stats.rx_time < 0 || stats.on_time_scan < 0 || energyUsed < 0) { - StringBuilder sb = new StringBuilder(); - sb.append(" rxIdleCur=" + rxIdleCurrent); - sb.append(" rxCur=" + rxCurrent); - sb.append(" txCur=" + txCurrent); - sb.append(" voltage=" + voltage); - sb.append(" on_time=" + stats.on_time); - sb.append(" tx_time=" + stats.tx_time); - sb.append(" tx_time_per_level=" + Arrays.toString(txTimePerLevel)); - sb.append(" rx_time=" + stats.rx_time); - sb.append(" rxIdleTime=" + rxIdleTime); - sb.append(" scan_time=" + stats.on_time_scan); - sb.append(" energy=" + energyUsed); - Log.d(TAG, " reportActivityInfo: " + sb.toString()); + String sb = " rxIdleCur=" + rxIdleCurrent + + " rxCur=" + rxCurrent + + " txCur=" + txCurrent + + " voltage=" + voltage + + " on_time=" + stats.on_time + + " tx_time=" + stats.tx_time + + " tx_time_per_level=" + Arrays.toString(txTimePerLevel) + + " rx_time=" + stats.rx_time + + " rxIdleTime=" + rxIdleTime + + " scan_time=" + stats.on_time_scan + + " energy=" + energyUsed; + Log.d(TAG, " reportActivityInfo: " + sb); } // Convert the LinkLayerStats into EnergyActivity @@ -1810,7 +1638,7 @@ public class WifiServiceImpl extends BaseWifiService { return null; } } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); + Log.e(TAG, "mClientModeImplChannel is not initialized"); return null; } } @@ -1831,7 +1659,7 @@ public class WifiServiceImpl extends BaseWifiService { try { mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid); } catch (SecurityException e) { - Slog.e(TAG, "Permission violation - getConfiguredNetworks not allowed for uid=" + Log.e(TAG, "Permission violation - getConfiguredNetworks not allowed for uid=" + callingUid + ", packageName=" + packageName + ", reason=" + e); return new ParceledListSlice<>(new ArrayList<>()); } finally { @@ -1853,32 +1681,27 @@ public class WifiServiceImpl extends BaseWifiService { } int targetConfigUid = Process.INVALID_UID; // don't expose any MAC addresses - if (isPrivileged(getCallingPid(), callingUid) || isDeviceOrProfileOwner(callingUid)) { + if (isPrivileged(getCallingPid(), callingUid) + || isDeviceOrProfileOwner(callingUid, packageName)) { targetConfigUid = Process.WIFI_UID; // expose all MAC addresses } else if (isCarrierApp) { targetConfigUid = callingUid; // expose only those configs created by the Carrier App } - - if (mClientModeImplChannel != null) { - List<WifiConfiguration> configs = mClientModeImpl.syncGetConfiguredNetworks( - callingUid, mClientModeImplChannel, targetConfigUid); - if (configs != null) { - if (isTargetSdkLessThanQOrPrivileged) { - return new ParceledListSlice<WifiConfiguration>(configs); - } else { // Carrier app: should only get its own configs - List<WifiConfiguration> creatorConfigs = new ArrayList<>(); - for (WifiConfiguration config : configs) { - if (config.creatorUid == callingUid) { - creatorConfigs.add(config); - } - } - return new ParceledListSlice<WifiConfiguration>(creatorConfigs); - } + int finalTargetConfigUid = targetConfigUid; + List<WifiConfiguration> configs = mWifiThreadRunner.call( + () -> mWifiConfigManager.getSavedNetworks(finalTargetConfigUid), + Collections.emptyList()); + if (isTargetSdkLessThanQOrPrivileged) { + return new ParceledListSlice<>(configs); + } + // Carrier app: should only get its own configs + List<WifiConfiguration> creatorConfigs = new ArrayList<>(); + for (WifiConfiguration config : configs) { + if (config.creatorUid == callingUid) { + creatorConfigs.add(config); } - } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); } - return null; + return new ParceledListSlice<>(creatorConfigs); } /** @@ -1888,8 +1711,8 @@ public class WifiServiceImpl extends BaseWifiService { * @return the list of configured networks with real preSharedKey */ @Override - public ParceledListSlice<WifiConfiguration> - getPrivilegedConfiguredNetworks(String packageName) { + public ParceledListSlice<WifiConfiguration> getPrivilegedConfiguredNetworks( + String packageName) { enforceReadCredentialPermission(); enforceAccessPermission(); int callingUid = Binder.getCallingUid(); @@ -1897,7 +1720,7 @@ public class WifiServiceImpl extends BaseWifiService { try { mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid); } catch (SecurityException e) { - Slog.e(TAG, "Permission violation - getPrivilegedConfiguredNetworks not allowed for" + Log.e(TAG, "Permission violation - getPrivilegedConfiguredNetworks not allowed for" + " uid=" + callingUid + ", packageName=" + packageName + ", reason=" + e); return null; } finally { @@ -1906,16 +1729,10 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getPrivilegedConfiguredNetworks uid=%").c(callingUid).flush(); } - if (mClientModeImplChannel != null) { - List<WifiConfiguration> configs = - mClientModeImpl.syncGetPrivilegedConfiguredNetwork(mClientModeImplChannel); - if (configs != null) { - return new ParceledListSlice<WifiConfiguration>(configs); - } - } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); - } - return null; + List<WifiConfiguration> configs = mWifiThreadRunner.call( + () -> mWifiConfigManager.getConfiguredNetworksWithPasswords(), + Collections.emptyList()); + return new ParceledListSlice<>(configs); } /** @@ -1938,11 +1755,9 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getMatchingPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush(); } - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)) { - return new HashMap<>(); - } - return mClientModeImpl.syncGetAllMatchingFqdnsForScanResults(scanResults, - mClientModeImplChannel); + return mWifiThreadRunner.call( + () -> mPasspointManager.getAllMatchingFqdnsForScanResults(scanResults), + Collections.emptyMap()); } /** @@ -1960,10 +1775,8 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getMatchingOsuProviders uid=%").c(Binder.getCallingUid()).flush(); } - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)) { - return new HashMap<>(); - } - return mClientModeImpl.syncGetMatchingOsuProviders(scanResults, mClientModeImplChannel); + return mWifiThreadRunner.call( + () -> mPasspointManager.getMatchingOsuProviders(scanResults), Collections.emptyMap()); } /** @@ -1982,15 +1795,13 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("getMatchingPasspointConfigsForOsuProviders uid=%").c( Binder.getCallingUid()).flush(); } - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)) { - return new HashMap<>(); - } if (osuProviders == null) { Log.e(TAG, "Attempt to retrieve Passpoint configuration with null osuProviders"); return new HashMap<>(); } - return mClientModeImpl.syncGetMatchingPasspointConfigsForOsuProviders(osuProviders, - mClientModeImplChannel); + return mWifiThreadRunner.call( + () -> mPasspointManager.getMatchingPasspointConfigsForOsuProviders(osuProviders), + Collections.emptyMap()); } /** @@ -2011,15 +1822,13 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("getWifiConfigsForPasspointProfiles uid=%").c( Binder.getCallingUid()).flush(); } - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)) { - return new ArrayList<>(); - } if (fqdnList == null) { Log.e(TAG, "Attempt to retrieve WifiConfiguration with null fqdn List"); return new ArrayList<>(); } - return mClientModeImpl.syncGetWifiConfigsForPasspointProfiles(fqdnList, - mClientModeImplChannel); + return mWifiThreadRunner.call( + () -> mPasspointManager.getWifiConfigsForPasspointProfiles(fqdnList), + Collections.emptyList()); } /** @@ -2032,8 +1841,9 @@ public class WifiServiceImpl extends BaseWifiService { if (enforceChangePermission(packageName) != MODE_ALLOWED) { return -1; } + int callingUid = Binder.getCallingUid(); if (!isTargetSdkLessThanQOrPrivileged( - packageName, Binder.getCallingPid(), Binder.getCallingUid())) { + packageName, Binder.getCallingPid(), callingUid)) { mLog.info("addOrUpdateNetwork not allowed for uid=%") .c(Binder.getCallingUid()).flush(); return -1; @@ -2041,7 +1851,7 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("addOrUpdateNetwork uid=%").c(Binder.getCallingUid()).flush(); if (config == null) { - Slog.e(TAG, "bad network configuration"); + Log.e(TAG, "bad network configuration"); return -1; } mWifiMetrics.incrementNumAddOrUpdateNetworkCalls(); @@ -2051,8 +1861,8 @@ public class WifiServiceImpl extends BaseWifiService { if (config.isPasspoint()) { PasspointConfiguration passpointConfig = PasspointProvider.convertFromWifiConfig(config); - if (passpointConfig.getCredential() == null) { - Slog.e(TAG, "Missing credential for Passpoint profile"); + if (passpointConfig == null || passpointConfig.getCredential() == null) { + Log.e(TAG, "Missing credential for Passpoint profile"); return -1; } @@ -2068,28 +1878,20 @@ public class WifiServiceImpl extends BaseWifiService { passpointConfig.getCredential().setClientPrivateKey( config.enterpriseConfig.getClientPrivateKey()); if (!addOrUpdatePasspointConfiguration(passpointConfig, packageName)) { - Slog.e(TAG, "Failed to add Passpoint profile"); + Log.e(TAG, "Failed to add Passpoint profile"); return -1; } // There is no network ID associated with a Passpoint profile. return 0; } - //TODO: pass the Uid the ClientModeImpl as a message parameter - Slog.i("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid()) + Log.i("addOrUpdateNetwork", " uid = " + Binder.getCallingUid() + " SSID " + config.SSID - + " nid=" + Integer.toString(config.networkId)); - if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) { - config.creatorUid = Binder.getCallingUid(); - } else { - config.lastUpdateUid = Binder.getCallingUid(); - } - if (mClientModeImplChannel != null) { - return mClientModeImpl.syncAddOrUpdateNetwork(mClientModeImplChannel, config); - } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); - return -1; - } + + " nid=" + config.networkId); + return mWifiThreadRunner.call( + () -> mWifiConfigManager.addOrUpdateNetwork(config, callingUid, packageName) + .getNetworkId(), + WifiConfiguration.INVALID_NETWORK_ID); } public static void verifyCert(X509Certificate caCert) @@ -2123,14 +1925,42 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); return false; } - mLog.info("removeNetwork uid=%").c(Binder.getCallingUid()).flush(); - // TODO Add private logging for netId b/33807876 - if (mClientModeImplChannel != null) { - return mClientModeImpl.syncRemoveNetwork(mClientModeImplChannel, netId); - } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); - return false; + int callingUid = Binder.getCallingUid(); + mLog.info("removeNetwork uid=%").c(callingUid).flush(); + return mWifiThreadRunner.call( + () -> mWifiConfigManager.removeNetwork(netId, callingUid, packageName), false); + } + + /** + * Trigger a connect request and wait for the callback to return status. + * This preserves the legacy connect API behavior, i.e. {@link WifiManager#enableNetwork( + * int, true)} + * @return + */ + private boolean triggerConnectAndReturnStatus(int netId, int callingUid) { + final CountDownLatch countDownLatch = new CountDownLatch(1); + final MutableBoolean success = new MutableBoolean(false); + IActionListener.Stub connectListener = new IActionListener.Stub() { + @Override + public void onSuccess() { + success.value = true; + countDownLatch.countDown(); + } + @Override + public void onFailure(int reason) { + success.value = false; + countDownLatch.countDown(); + } + }; + mClientModeImpl.connect(null, netId, new Binder(), connectListener, + connectListener.hashCode(), callingUid); + // now wait for response. + try { + countDownLatch.await(RUN_WITH_SCISSORS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "Failed to retrieve connect status"); } + return success.value; } /** @@ -2151,18 +1981,19 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); return false; } + int callingUid = Binder.getCallingUid(); // TODO b/33807876 Log netId mLog.info("enableNetwork uid=% disableOthers=%") - .c(Binder.getCallingUid()) + .c(callingUid) .c(disableOthers).flush(); mWifiMetrics.incrementNumEnableNetworkCalls(); - if (mClientModeImplChannel != null) { - return mClientModeImpl.syncEnableNetwork(mClientModeImplChannel, netId, - disableOthers); + if (disableOthers) { + return triggerConnectAndReturnStatus(netId, callingUid); } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); - return false; + return mWifiThreadRunner.call( + () -> mWifiConfigManager.enableNetwork(netId, false, callingUid, packageName), + false); } } @@ -2183,15 +2014,25 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); return false; } - // TODO b/33807876 Log netId - mLog.info("disableNetwork uid=%").c(Binder.getCallingUid()).flush(); + int callingUid = Binder.getCallingUid(); + mLog.info("disableNetwork uid=%").c(callingUid).flush(); + return mWifiThreadRunner.call( + () -> mWifiConfigManager.disableNetwork(netId, callingUid, packageName), false); + } - if (mClientModeImplChannel != null) { - return mClientModeImpl.syncDisableNetwork(mClientModeImplChannel, netId); - } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); - return false; - } + /** + * See {@link android.net.wifi.WifiManager#allowAutojoin(int, boolean)} + * @param netId the integer that identifies the network configuration + * @param choice the user's choice to allow auto-join + */ + @Override + public void allowAutojoin(int netId, boolean choice) { + enforceNetworkSettingsPermission(); + + int callingUid = Binder.getCallingUid(); + mLog.info("allowAutojoin=% uid=%").c(choice).c(callingUid).flush(); + mWifiThreadRunner.post( + () -> mWifiConfigManager.allowAutojoin(netId, choice)); } /** @@ -2218,9 +2059,7 @@ public class WifiServiceImpl extends BaseWifiService { } mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, uid); hideBssidSsidAndNetworkId = false; - } catch (RemoteException e) { - Log.e(TAG, "Error checking receiver permission", e); - } catch (SecurityException e) { + } catch (SecurityException ignored) { } if (hideDefaultMacAddress) { result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS); @@ -2257,19 +2096,13 @@ public class WifiServiceImpl extends BaseWifiService { } try { mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, uid); - final List<ScanResult> scanResults = new ArrayList<>(); - boolean success = mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - scanResults.addAll(mScanRequestProxy.getScanResults()); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!success) { - Log.e(TAG, "Failed to post runnable to fetch scan results"); - return new ArrayList<ScanResult>(); - } + List<ScanResult> scanResults = mWifiThreadRunner.call( + mScanRequestProxy::getScanResults, Collections.emptyList()); return scanResults; } catch (SecurityException e) { - Slog.e(TAG, "Permission violation - getScanResults not allowed for uid=" + Log.e(TAG, "Permission violation - getScanResults not allowed for uid=" + uid + ", packageName=" + callingPackage + ", reason=" + e); - return new ArrayList<ScanResult>(); + return new ArrayList<>(); } finally { Binder.restoreCallingIdentity(ident); } @@ -2287,13 +2120,11 @@ public class WifiServiceImpl extends BaseWifiService { if (enforceChangePermission(packageName) != MODE_ALLOWED) { return false; } - mLog.info("addorUpdatePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush(); - if (!mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)) { - return false; - } - return mClientModeImpl.syncAddOrUpdatePasspointConfig(mClientModeImplChannel, config, - Binder.getCallingUid(), packageName); + int callingUid = Binder.getCallingUid(); + mLog.info("addorUpdatePasspointConfiguration uid=%").c(callingUid).flush(); + return mWifiThreadRunner.call( + () -> mPasspointManager.addOrUpdateProvider(config, callingUid, packageName, false), + false); } /** @@ -2311,10 +2142,9 @@ public class WifiServiceImpl extends BaseWifiService { privileged = true; } mLog.info("removePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush(); - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)) { - return false; - } - return mClientModeImpl.syncRemovePasspointConfig(mClientModeImplChannel, privileged, fqdn); + final boolean privilegedFinal = privileged; + return mWifiThreadRunner.call( + () -> mPasspointManager.removeProvider(uid, privilegedFinal, fqdn), false); } /** @@ -2335,11 +2165,10 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush(); } - if (!mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)) { - return new ArrayList<>(); - } - return mClientModeImpl.syncGetPasspointConfigs(mClientModeImplChannel, privileged); + final boolean privilegedFinal = privileged; + return mWifiThreadRunner.call( + () -> mPasspointManager.getProviderConfigs(uid, privilegedFinal), + Collections.emptyList()); } /** @@ -2351,10 +2180,6 @@ public class WifiServiceImpl extends BaseWifiService { public void queryPasspointIcon(long bssid, String fileName) { enforceAccessPermission(); mLog.info("queryPasspointIcon uid=%").c(Binder.getCallingUid()).flush(); - if (!mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)) { - throw new UnsupportedOperationException("Passpoint not enabled"); - } mClientModeImpl.syncQueryPasspointIcon(mClientModeImplChannel, bssid, fileName); } @@ -2366,7 +2191,7 @@ public class WifiServiceImpl extends BaseWifiService { @Override public int matchProviderWithCurrentNetwork(String fqdn) { mLog.info("matchProviderWithCurrentNetwork uid=%").c(Binder.getCallingUid()).flush(); - return mClientModeImpl.matchProviderWithCurrentNetwork(mClientModeImplChannel, fqdn); + return 0; } /** @@ -2380,21 +2205,6 @@ public class WifiServiceImpl extends BaseWifiService { mClientModeImpl.deauthenticateNetwork(mClientModeImplChannel, holdoff, ess); } - /** - * Set the country code - * @param countryCode ISO 3166 country code. - * - */ - @Override - public void setCountryCode(String countryCode) { - Slog.i(TAG, "WifiService trying to set country code to " + countryCode); - enforceConnectivityInternalPermission(); - mLog.info("setCountryCode uid=%").c(Binder.getCallingUid()).flush(); - final long token = Binder.clearCallingIdentity(); - mCountryCode.setCountryCode(countryCode); - Binder.restoreCallingIdentity(token); - } - /** * Get the country code * @return Get the best choice country code for wifi, regardless of if it was set or @@ -2407,8 +2217,7 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("getCountryCode uid=%").c(Binder.getCallingUid()).flush(); } - String country = mCountryCode.getCountryCode(); - return country; + return mCountryCode.getCountryCode(); } @Override @@ -2503,31 +2312,28 @@ public class WifiServiceImpl extends BaseWifiService { * so we need to do additional work to find it from remote IP address */ - class TdlsTaskParams { - public String remoteIpAddress; - public boolean enable; + private static class TdlsTaskParams { + String mRemoteIpAddress; + boolean mEnable; } - class TdlsTask extends AsyncTask<TdlsTaskParams, Integer, Integer> { + private class TdlsTask extends AsyncTask<TdlsTaskParams, Integer, Integer> { @Override protected Integer doInBackground(TdlsTaskParams... params) { // Retrieve parameters for the call TdlsTaskParams param = params[0]; - String remoteIpAddress = param.remoteIpAddress.trim(); - boolean enable = param.enable; + String remoteIpAddress = param.mRemoteIpAddress.trim(); + boolean enable = param.mEnable; // Get MAC address of Remote IP String macAddress = null; - BufferedReader reader = null; - - try { - reader = new BufferedReader(new FileReader("/proc/net/arp")); - - // Skip over the line bearing colum titles - String line = reader.readLine(); + try (BufferedReader reader = new BufferedReader(new FileReader("/proc/net/arp"))) { + // Skip over the line bearing column titles + reader.readLine(); + String line; while ((line = reader.readLine()) != null) { String[] tokens = line.split("[ ]+"); if (tokens.length < 6) { @@ -2546,27 +2352,17 @@ public class WifiServiceImpl extends BaseWifiService { } if (macAddress == null) { - Slog.w(TAG, "Did not find remoteAddress {" + remoteIpAddress + "} in " + - "/proc/net/arp"); + Log.w(TAG, "Did not find remoteAddress {" + remoteIpAddress + "} in " + + "/proc/net/arp"); } else { enableTdlsWithMacAddress(macAddress, enable); } } catch (FileNotFoundException e) { - Slog.e(TAG, "Could not open /proc/net/arp to lookup mac address"); + Log.e(TAG, "Could not open /proc/net/arp to lookup mac address"); } catch (IOException e) { - Slog.e(TAG, "Could not read /proc/net/arp to lookup mac address"); - } finally { - try { - if (reader != null) { - reader.close(); - } - } - catch (IOException e) { - // Do nothing - } + Log.e(TAG, "Could not read /proc/net/arp to lookup mac address"); } - return 0; } } @@ -2578,8 +2374,8 @@ public class WifiServiceImpl extends BaseWifiService { } mLog.info("enableTdls uid=% enable=%").c(Binder.getCallingUid()).c(enable).flush(); TdlsTaskParams params = new TdlsTaskParams(); - params.remoteIpAddress = remoteAddress; - params.enable = enable; + params.mRemoteIpAddress = remoteAddress; + params.mEnable = enable; new TdlsTask().execute(params); } @@ -2598,22 +2394,6 @@ public class WifiServiceImpl extends BaseWifiService { } /** - * Get a reference to handler. This is used by a client to establish - * an AsyncChannel communication with WifiService - */ - @Override - public Messenger getWifiServiceMessenger(String packageName) { - enforceAccessPermission(); - if (enforceChangePermission(packageName) != MODE_ALLOWED) { - // We don't have a good way of creating a fake Messenger, and returning null would - // immediately break callers. - throw new SecurityException("Could not create wifi service messenger"); - } - mLog.info("getWifiServiceMessenger uid=%").c(Binder.getCallingUid()).flush(); - return new Messenger(mAsyncChannelExternalClientHandler); - } - - /** * Disable an ephemeral network, i.e. network that is created thru a WiFi Scorer */ @Override @@ -2626,7 +2406,7 @@ public class WifiServiceImpl extends BaseWifiService { return; } mLog.info("disableEphemeralNetwork uid=%").c(Binder.getCallingUid()).flush(); - mClientModeImpl.disableEphemeralNetwork(SSID); + mWifiThreadRunner.post(() -> mWifiConfigManager.disableEphemeralNetwork(SSID)); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -2635,17 +2415,19 @@ public class WifiServiceImpl extends BaseWifiService { String action = intent.getAction(); if (action.equals(Intent.ACTION_USER_REMOVED)) { int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - mClientModeImpl.removeUserConfigs(userHandle); + mWifiThreadRunner.post(() -> mWifiConfigManager.removeNetworksForUser(userHandle)); } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.STATE_DISCONNECTED); mClientModeImpl.sendBluetoothAdapterStateChange(state); } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { - boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false); - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0); + boolean emergencyMode = + intent.getBooleanExtra(PhoneConstants.PHONE_IN_ECM_STATE, false); + mActiveModeWarden.emergencyCallbackModeChanged(emergencyMode); } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED)) { - boolean inCall = intent.getBooleanExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, false); - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, inCall ? 1 : 0, 0); + boolean inCall = + intent.getBooleanExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, false); + mActiveModeWarden.emergencyCallStateChanged(inCall); } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { handleIdleModeChanged(); } @@ -2660,7 +2442,7 @@ public class WifiServiceImpl extends BaseWifiService { @Override public void onChange(boolean selfChange) { mSettingsStore.handleWifiScanAlwaysAvailableToggled(); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); + mActiveModeWarden.scanAlwaysModeChanged(); } }; mFrameworkFacade.registerContentObserver(mContext, @@ -2698,10 +2480,13 @@ public class WifiServiceImpl extends BaseWifiService { return; } String pkgName = uri.getSchemeSpecificPart(); - mClientModeImpl.removeAppConfigs(pkgName, uid); - // Call the method in ClientModeImpl thread. - mWifiInjector.getClientModeImplHandler().post(() -> { + // Call the method in the main Wifi thread. + mWifiThreadRunner.post(() -> { + ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = pkgName; + ai.uid = uid; + mWifiConfigManager.removeNetworksForApp(ai); mScanRequestProxy.clearScanRequestTimestampsForApp(pkgName, uid); // Remove all suggestions from the package. @@ -2711,7 +2496,6 @@ public class WifiServiceImpl extends BaseWifiService { // Remove all Passpoint profiles from package. mWifiInjector.getPasspointManager().removePasspointProviderWithPackage( pkgName); - }); } } @@ -2736,7 +2520,11 @@ public class WifiServiceImpl extends BaseWifiService { } if (args != null && args.length > 0 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0])) { // WifiMetrics proto bytes were requested. Dump only these. - mClientModeImpl.updateWifiMetrics(); + mWifiThreadRunner.run(() -> { + mWifiMetrics.updateSavedNetworks( + mWifiConfigManager.getSavedNetworks(Process.WIFI_UID)); + mPasspointManager.updateMetrics(); + }); mWifiMetrics.dump(fd, pw, args); } else if (args != null && args.length > 0 && IpClientUtil.DUMP_ARG.equals(args[0])) { // IpClient dump was requested. Pass it along and take no further action. @@ -2747,12 +2535,10 @@ public class WifiServiceImpl extends BaseWifiService { WifiScoreReport wifiScoreReport = mClientModeImpl.getWifiScoreReport(); if (wifiScoreReport != null) wifiScoreReport.dump(fd, pw, args); } else if (args != null && args.length > 0 && WifiScoreCard.DUMP_ARG.equals(args[0])) { - mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); - if (wifiScoreCard != null) { - pw.println(wifiScoreCard.getNetworkListBase64(true)); - } - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); + String networkListBase64 = mWifiThreadRunner.call(() -> + wifiScoreCard.getNetworkListBase64(true), ""); + pw.println(networkListBase64); } else { // Polls link layer stats and RSSI. This allows the stats to show up in // WifiScoreReport's dump() output when taking a bug report even if the screen is off. @@ -2764,7 +2550,6 @@ public class WifiServiceImpl extends BaseWifiService { 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); mWifiTrafficPoller.dump(fd, pw, args); pw.println(); @@ -2777,35 +2562,31 @@ public class WifiServiceImpl extends BaseWifiService { pw.println(); mClientModeImpl.dump(fd, pw, args); pw.println(); - mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); - if (wifiScoreCard != null) { - pw.println("WifiScoreCard:"); - pw.println(wifiScoreCard.getNetworkListBase64(true)); - } - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - mClientModeImpl.updateWifiMetrics(); + WifiScoreCard wifiScoreCard = mWifiInjector.getWifiScoreCard(); + String networkListBase64 = mWifiThreadRunner.call(() -> + wifiScoreCard.getNetworkListBase64(true), ""); + pw.println("WifiScoreCard:"); + pw.println(networkListBase64); + mWifiThreadRunner.run(() -> { + mWifiMetrics.updateSavedNetworks( + mWifiConfigManager.getSavedNetworks(Process.WIFI_UID)); + mPasspointManager.updateMetrics(); + }); mWifiMetrics.dump(fd, pw, args); pw.println(); - mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - mWifiNetworkSuggestionsManager.dump(fd, pw, args); - pw.println(); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + mWifiThreadRunner.run(() -> mWifiNetworkSuggestionsManager.dump(fd, pw, args)); + pw.println(); mWifiBackupRestore.dump(fd, pw, args); pw.println(); pw.println("ScoringParams: settings put global " + Settings.Global.WIFI_SCORE_PARAMS + " " + mWifiInjector.getScoringParams()); pw.println(); + pw.println("WifiScoreReport:"); WifiScoreReport wifiScoreReport = mClientModeImpl.getWifiScoreReport(); - if (wifiScoreReport != null) { - pw.println("WifiScoreReport:"); - wifiScoreReport.dump(fd, pw, args); - } + wifiScoreReport.dump(fd, pw, args); pw.println(); SarManager sarManager = mWifiInjector.getSarManager(); - if (sarManager != null) { - sarManager.dump(fd, pw, args); - } + sarManager.dump(fd, pw, args); pw.println(); } } @@ -2823,18 +2604,8 @@ public class WifiServiceImpl extends BaseWifiService { WorkSource updatedWs = (ws == null || ws.isEmpty()) ? new WorkSource(Binder.getCallingUid()) : ws; - Mutable<Boolean> lockSuccess = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - lockSuccess.value = mWifiLockManager.acquireWifiLock( - lockMode, tag, binder, updatedWs); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to acquireWifiLock"); - return false; - } - - return lockSuccess.value; + return mWifiThreadRunner.call(() -> + mWifiLockManager.acquireWifiLock(lockMode, tag, binder, updatedWs), false); } @Override @@ -2849,13 +2620,8 @@ public class WifiServiceImpl extends BaseWifiService { WorkSource updatedWs = (ws == null || ws.isEmpty()) ? new WorkSource(Binder.getCallingUid()) : ws; - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - mWifiLockManager.updateWifiLockWorkSource(binder, updatedWs); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to updateWifiLockWorkSource"); - } + mWifiThreadRunner.run(() -> + mWifiLockManager.updateWifiLockWorkSource(binder, updatedWs)); } @Override @@ -2864,16 +2630,9 @@ public class WifiServiceImpl extends BaseWifiService { // Check on permission to make this call mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - Mutable<Boolean> lockSuccess = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - lockSuccess.value = mWifiLockManager.releaseWifiLock(binder); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to releaseWifiLock"); - return false; - } - return lockSuccess.value; + + return mWifiThreadRunner.call(() -> + mWifiLockManager.releaseWifiLock(binder), false); } @Override @@ -2918,7 +2677,7 @@ public class WifiServiceImpl extends BaseWifiService { enableVerboseLoggingInternal(verbose); } - void enableVerboseLoggingInternal(int verbose) { + private void enableVerboseLoggingInternal(int verbose) { mVerboseLoggingEnabled = verbose > 0; mClientModeImpl.enableVerboseLogging(verbose); mWifiLockManager.enableVerboseLogging(verbose); @@ -2945,44 +2704,35 @@ public class WifiServiceImpl extends BaseWifiService { if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) { return; } - if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) { // Turn mobile hotspot off stopSoftApInternal(WifiManager.IFACE_IP_MODE_UNSPECIFIED); } - if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) { - if (mClientModeImplChannel != null) { - // Delete all Wifi SSIDs - List<WifiConfiguration> networks = mClientModeImpl.syncGetConfiguredNetworks( - Binder.getCallingUid(), mClientModeImplChannel, Process.WIFI_UID); - if (networks != null) { - for (WifiConfiguration config : networks) { - removeNetwork(config.networkId, packageName); - } - } - - // Delete all Passpoint configurations - if (mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)) { - List<PasspointConfiguration> configs = mClientModeImpl.syncGetPasspointConfigs( - mClientModeImplChannel, true); - if (configs != null) { - for (PasspointConfiguration config : configs) { - removePasspointConfiguration(config.getHomeSp().getFqdn(), packageName); - } - } - } - } - - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiInjector.getWifiConfigManager().clearDeletedEphemeralNetworks(); - mClientModeImpl.clearNetworkRequestUserApprovedAccessPoints(); - mWifiNetworkSuggestionsManager.clear(); - mWifiInjector.getWifiScoreCard().clear(); - }); - notifyFactoryReset(); + if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) { + return; } + // Delete all Wifi SSIDs + List<WifiConfiguration> networks = mWifiThreadRunner.call( + () -> mWifiConfigManager.getSavedNetworks(Process.WIFI_UID), + Collections.emptyList()); + for (WifiConfiguration network : networks) { + removeNetwork(network.networkId, packageName); + } + // Delete all Passpoint configurations + List<PasspointConfiguration> configs = mWifiThreadRunner.call( + () -> mPasspointManager.getProviderConfigs(Process.WIFI_UID /* ignored */, true), + Collections.emptyList()); + for (PasspointConfiguration config : configs) { + removePasspointConfiguration(config.getHomeSp().getFqdn(), packageName); + } + mWifiThreadRunner.post(() -> { + mWifiConfigManager.clearDeletedEphemeralNetworks(); + mClientModeImpl.clearNetworkRequestUserApprovedAccessPoints(); + mWifiNetworkSuggestionsManager.clear(); + mWifiInjector.getWifiScoreCard().clear(); + notifyFactoryReset(); + }); } /** @@ -2995,12 +2745,6 @@ public class WifiServiceImpl extends BaseWifiService { android.Manifest.permission.NETWORK_CARRIER_PROVISIONING); } - /* private methods */ - static boolean logAndReturnFalse(String s) { - Log.d(TAG, s); - return false; - } - @Override public Network getCurrentNetwork() { enforceAccessPermission(); @@ -3046,16 +2790,16 @@ public class WifiServiceImpl extends BaseWifiService { enforceNetworkSettingsPermission(); mLog.info("retrieveBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mClientModeImplChannel == null) { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); + Log.e(TAG, "mClientModeImplChannel is not initialized"); return null; } - Slog.d(TAG, "Retrieving backup data"); - List<WifiConfiguration> wifiConfigurations = - mClientModeImpl.syncGetPrivilegedConfiguredNetwork(mClientModeImplChannel); + Log.d(TAG, "Retrieving backup data"); + List<WifiConfiguration> wifiConfigurations = mWifiThreadRunner.call( + () -> mWifiConfigManager.getConfiguredNetworksWithPasswords(), null); byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(wifiConfigurations); - Slog.d(TAG, "Retrieved backup data"); + Log.d(TAG, "Retrieved backup data"); return backupData; } @@ -3066,19 +2810,24 @@ public class WifiServiceImpl extends BaseWifiService { */ private void restoreNetworks(List<WifiConfiguration> configurations) { if (configurations == null) { - Slog.e(TAG, "Backup data parse failed"); + Log.e(TAG, "Backup data parse failed"); return; } - for (WifiConfiguration configuration : configurations) { - int networkId = mClientModeImpl.syncAddOrUpdateNetwork( - mClientModeImplChannel, configuration); - if (networkId == WifiConfiguration.INVALID_NETWORK_ID) { - Slog.e(TAG, "Restore network failed: " + configuration.configKey()); - continue; - } - // Enable all networks restored. - mClientModeImpl.syncEnableNetwork(mClientModeImplChannel, networkId, false); - } + int callingUid = Binder.getCallingUid(); + mWifiThreadRunner.run( + () -> { + for (WifiConfiguration configuration : configurations) { + int networkId = + mWifiConfigManager.addOrUpdateNetwork(configuration, callingUid) + .getNetworkId(); + if (networkId == WifiConfiguration.INVALID_NETWORK_ID) { + Log.e(TAG, "Restore network failed: " + configuration.configKey()); + continue; + } + // Enable all networks restored. + mWifiConfigManager.enableNetwork(networkId, false, callingUid, null); + } + }); } /** @@ -3091,15 +2840,15 @@ public class WifiServiceImpl extends BaseWifiService { enforceNetworkSettingsPermission(); mLog.info("restoreBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mClientModeImplChannel == null) { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); + Log.e(TAG, "mClientModeImplChannel is not initialized"); return; } - Slog.d(TAG, "Restoring backup data"); + Log.d(TAG, "Restoring backup data"); List<WifiConfiguration> wifiConfigurations = mWifiBackupRestore.retrieveConfigurationsFromBackupData(data); restoreNetworks(wifiConfigurations); - Slog.d(TAG, "Restored backup data"); + Log.d(TAG, "Restored backup data"); } /** @@ -3113,16 +2862,16 @@ public class WifiServiceImpl extends BaseWifiService { enforceNetworkSettingsPermission(); mLog.trace("restoreSupplicantBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mClientModeImplChannel == null) { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); + Log.e(TAG, "mClientModeImplChannel is not initialized"); return; } - Slog.d(TAG, "Restoring supplicant backup data"); + Log.d(TAG, "Restoring supplicant backup data"); List<WifiConfiguration> wifiConfigurations = mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData( supplicantData, ipConfigData); restoreNetworks(wifiConfigurations); - Slog.d(TAG, "Restored supplicant backup data"); + Log.d(TAG, "Restored supplicant backup data"); } /** @@ -3143,10 +2892,6 @@ public class WifiServiceImpl extends BaseWifiService { if (!isSettingsOrSuw(Binder.getCallingPid(), Binder.getCallingUid())) { throw new SecurityException(TAG + ": Permission denied"); } - if (!mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)) { - throw new UnsupportedOperationException("Passpoint not enabled"); - } final int uid = Binder.getCallingUid(); mLog.trace("startSubscriptionProvisioning uid=%").c(uid).flush(); if (mClientModeImpl.syncStartSubscriptionProvisioning(uid, provider, @@ -3184,9 +2929,8 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("registerTrafficStateCallback uid=%").c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiTrafficPoller.addCallback(binder, callback, callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiTrafficPoller.addCallback(binder, callback, callbackIdentifier)); } /** @@ -3204,9 +2948,8 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("unregisterTrafficStateCallback uid=%").c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiTrafficPoller.removeCallback(callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiTrafficPoller.removeCallback(callbackIdentifier)); } private boolean is5GhzSupported() { @@ -3218,7 +2961,7 @@ public class WifiServiceImpl extends BaseWifiService { if (channel != null) { return mClientModeImpl.syncGetSupportedFeatures(channel); } else { - Slog.e(TAG, "mClientModeImplChannel is not initialized"); + Log.e(TAG, "mClientModeImplChannel is not initialized"); return 0; } } @@ -3258,9 +3001,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mClientModeImpl.addNetworkRequestMatchCallback(binder, callback, callbackIdentifier); - }); + mWifiThreadRunner.post(() -> mClientModeImpl.addNetworkRequestMatchCallback( + binder, callback, callbackIdentifier)); } /** @@ -3279,9 +3021,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mClientModeImpl.removeNetworkRequestMatchCallback(callbackIdentifier); - }); + mWifiThreadRunner.post(() -> + mClientModeImpl.removeNetworkRequestMatchCallback(callbackIdentifier)); } /** @@ -3302,20 +3043,14 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("addNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush(); } int callingUid = Binder.getCallingUid(); - Mutable<Integer> success = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - success.value = mWifiNetworkSuggestionsManager.add( - networkSuggestions, callingUid, callingPackageName); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to add network suggestions"); - return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; - } - if (success.value != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { + + int success = mWifiThreadRunner.call(() -> mWifiNetworkSuggestionsManager.add( + networkSuggestions, callingUid, callingPackageName), + WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL); + if (success != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { Log.e(TAG, "Failed to add network suggestions"); } - return success.value; + return success; } /** @@ -3336,20 +3071,29 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("removeNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush(); } int callingUid = Binder.getCallingUid(); - Mutable<Integer> success = new Mutable<>(); - boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors( - () -> { - success.value = mWifiNetworkSuggestionsManager.remove( - networkSuggestions, callingUid, callingPackageName); - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (!runWithScissorsSuccess) { - Log.e(TAG, "Failed to post runnable to remove network suggestions"); - return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; - } - if (success.value != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { + + int success = mWifiThreadRunner.call(() -> mWifiNetworkSuggestionsManager.remove( + networkSuggestions, callingUid, callingPackageName), + WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL); + if (success != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { Log.e(TAG, "Failed to remove network suggestions"); } - return success.value; + return success; + } + + /** + * See {@link android.net.wifi.WifiManager#getNetworkSuggestions()} + * @param callingPackageName Package Name of the app getting the suggestions. + * @return a list of network suggestions suggested by this app + */ + public List<WifiNetworkSuggestion> getNetworkSuggestions(String callingPackageName) { + mAppOps.checkPackage(Binder.getCallingUid(), callingPackageName); + enforceAccessPermission(); + if (mVerboseLoggingEnabled) { + mLog.info("getNetworkSuggestionList uid=%").c(Binder.getCallingUid()).flush(); + } + return mWifiThreadRunner.call(() -> + mWifiNetworkSuggestionsManager.get(callingPackageName), Collections.emptyList()); } /** @@ -3364,17 +3108,14 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException("App not allowed to get Wi-Fi factory MAC address " + "(uid = " + uid + ")"); } - final List<String> result = new ArrayList<>(); - boolean success = mWifiInjector.getClientModeImplHandler().runWithScissors(() -> { - final String mac = mClientModeImpl.getFactoryMacAddress(); - if (mac != null) { - result.add(mac); - } - }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); - if (success) { - return result.isEmpty() ? null : result.stream().toArray(String[]::new); + String result = mWifiThreadRunner.call(mClientModeImpl::getFactoryMacAddress, null); + // result can be null if either: WifiThreadRunner.call() timed out, or + // ClientModeImpl.getFactoryMacAddress() returned null. + // In this particular instance, we don't differentiate the two types of nulls. + if (result == null) { + return null; } - return null; + return new String[]{result}; } /** @@ -3393,8 +3134,7 @@ public class WifiServiceImpl extends BaseWifiService { .flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post( - () -> mClientModeImpl.setDeviceMobilityState(state)); + mWifiThreadRunner.post(() -> mClientModeImpl.setDeviceMobilityState(state)); } /** @@ -3438,10 +3178,8 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException(TAG + ": Permission denied"); } - mDppManager.mHandler.post(() -> { - mDppManager.startDppAsConfiguratorInitiator(uid, binder, enrolleeUri, - selectedNetworkId, netRole, callback); - }); + mWifiThreadRunner.post(() -> mDppManager.startDppAsConfiguratorInitiator( + uid, binder, enrolleeUri, selectedNetworkId, netRole, callback)); } /** @@ -3472,24 +3210,21 @@ public class WifiServiceImpl extends BaseWifiService { throw new SecurityException(TAG + ": Permission denied"); } - mDppManager.mHandler.post(() -> { - mDppManager.startDppAsEnrolleeInitiator(uid, binder, configuratorUri, callback); - }); + mWifiThreadRunner.post(() -> + mDppManager.startDppAsEnrolleeInitiator(uid, binder, configuratorUri, callback)); } /** * Stop or abort a current DPP session. */ @Override - public void stopDppSession() throws android.os.RemoteException { + public void stopDppSession() throws RemoteException { if (!isSettingsOrSuw(Binder.getCallingPid(), Binder.getCallingUid())) { throw new SecurityException(TAG + ": Permission denied"); } final int uid = getMockableCallingUid(); - mDppManager.mHandler.post(() -> { - mDppManager.stopDppSession(uid); - }); + mWifiThreadRunner.post(() -> mDppManager.stopDppSession(uid)); } /** @@ -3522,9 +3257,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiMetrics.addOnWifiUsabilityListener(binder, listener, listenerIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiMetrics.addOnWifiUsabilityListener(binder, listener, listenerIdentifier)); } /** @@ -3544,9 +3278,8 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()).flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post(() -> { - mWifiMetrics.removeOnWifiUsabilityListener(listenerIdentifier); - }); + mWifiThreadRunner.post(() -> + mWifiMetrics.removeOnWifiUsabilityListener(listenerIdentifier)); } /** @@ -3569,8 +3302,116 @@ public class WifiServiceImpl extends BaseWifiService { .flush(); } // Post operation to handler thread - mWifiInjector.getClientModeImplHandler().post( - () -> mClientModeImpl.updateWifiUsabilityScore(seqNum, score, - predictionHorizonSec)); + mWifiThreadRunner.post(() -> + mClientModeImpl.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec)); + } + + /** + * see {@link android.net.wifi.WifiManager#connect(int, WifiManager.ActionListener)} + */ + @Override + public void connect(WifiConfiguration config, int netId, IBinder binder, + @Nullable IActionListener callback, int callbackIdentifier) { + if (!isPrivileged(Binder.getCallingPid(), Binder.getCallingUid())) { + throw new SecurityException(TAG + ": Permission denied"); + } + if (mVerboseLoggingEnabled) { + mLog.info("connect uid=%").c(Binder.getCallingUid()).flush(); + } + mClientModeImpl.connect( + config, netId, binder, callback, callbackIdentifier, Binder.getCallingUid()); + } + + /** + * see {@link android.net.wifi.WifiManager#save(WifiConfiguration, + * WifiManager.ActionListener)} + */ + @Override + public void save(WifiConfiguration config, IBinder binder, @Nullable IActionListener callback, + int callbackIdentifier) { + if (!isPrivileged(Binder.getCallingPid(), Binder.getCallingUid())) { + throw new SecurityException(TAG + ": Permission denied"); + } + if (mVerboseLoggingEnabled) { + mLog.info("connect uid=%").c(Binder.getCallingUid()).flush(); + } + mClientModeImpl.save( + config, binder, callback, callbackIdentifier, Binder.getCallingUid()); + } + + /** + * see {@link android.net.wifi.WifiManager#forget(int, WifiManager.ActionListener)} + */ + @Override + public void forget(int netId, IBinder binder, @Nullable IActionListener callback, + int callbackIdentifier) { + if (!isPrivileged(Binder.getCallingPid(), Binder.getCallingUid())) { + throw new SecurityException(TAG + ": Permission denied"); + } + if (mVerboseLoggingEnabled) { + mLog.info("connect uid=%").c(Binder.getCallingUid()).flush(); + } + mClientModeImpl.forget( + netId, binder, callback, callbackIdentifier, Binder.getCallingUid()); + } + + /** + * see {@link android.net.wifi.WifiManager#getTxPacketCount(WifiManager.TxPacketCountListener)} + */ + @Override + public void getTxPacketCount(String packageName, IBinder binder, + @NonNull ITxPacketCountListener callback, int callbackIdentifier) { + // verify arguments + if (binder == null) { + throw new IllegalArgumentException("Binder must not be null"); + } + if (callback == null) { + throw new IllegalArgumentException("Callback must not be null"); + } + enforceChangePermission(packageName); + if (mVerboseLoggingEnabled) { + mLog.info("connect uid=%").c(Binder.getCallingUid()).flush(); + } + mClientModeImpl.getTxPacketCount( + binder, callback, callbackIdentifier, Binder.getCallingUid()); + } + + /** + * See {@link WifiManager#addScanResultsListener(Executor, WifiManager.ScanResultsListener)} + */ + public void registerScanResultsListener(IBinder binder, IScanResultsListener listener, + int listenerIdentifier) { + if (binder == null) { + throw new IllegalArgumentException("Binder must not be null"); + } + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + enforceAccessPermission(); + + if (mVerboseLoggingEnabled) { + mLog.info("registerScanResultListener uid=%").c(Binder.getCallingUid()).flush(); + } + mWifiThreadRunner.post(() -> { + if (!mWifiInjector.getScanRequestProxy().registerScanResultsListener(binder, listener, + listenerIdentifier)) { + Log.e(TAG, "registerScanResultListener: Failed to add callback"); + } + }); + } + + /** + * See {@link WifiManager#removeScanResultsListener(WifiManager.ScanResultsListener)} + */ + public void unregisterScanResultsListener(int listenerIdentifier) { + if (mVerboseLoggingEnabled) { + mLog.info("unregisterScanResultCallback uid=%").c(Binder.getCallingUid()).flush(); + } + enforceAccessPermission(); + // post operation to handler thread + mWifiThreadRunner.post(() -> + mWifiInjector.getScanRequestProxy() + .unregisterScanResultsListener(listenerIdentifier)); + } } diff --git a/service/java/com/android/server/wifi/WifiSettingsStore.java b/service/java/com/android/server/wifi/WifiSettingsStore.java index 8d8d587b5c..a2f962f620 100644 --- a/service/java/com/android/server/wifi/WifiSettingsStore.java +++ b/service/java/com/android/server/wifi/WifiSettingsStore.java @@ -45,9 +45,6 @@ public class WifiSettingsStore { private final Context mContext; - /* Tracks if we have checked the saved wi-fi state after boot */ - private boolean mCheckSavedStateAtBoot = false; - WifiSettingsStore(Context context) { mContext = context; mAirplaneModeOn = getPersistedAirplaneModeOn(); @@ -56,11 +53,6 @@ public class WifiSettingsStore { } public synchronized boolean isWifiToggleEnabled() { - if (!mCheckSavedStateAtBoot) { - mCheckSavedStateAtBoot = true; - if (testAndClearWifiSavedState()) return true; - } - if (mAirplaneModeOn) { return mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE; } else { @@ -116,8 +108,7 @@ public class WifiSettingsStore { } } else { /* On airplane mode disable, restore wifi state if necessary */ - if (testAndClearWifiSavedState() || - mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE + if (mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE || mPersistWifiState == WIFI_DISABLED_AIRPLANE_ON) { persistWifiState(WIFI_ENABLED); } @@ -156,55 +147,6 @@ public class WifiSettingsStore { && toggleableRadios.contains(Settings.Global.RADIO_WIFI); } - /** - * After a reboot, we restore wi-fi to be on if it was turned off temporarily for tethering. - * The settings app tracks the saved state, but the framework has to check it at boot to - * make sure the wi-fi is turned on in case it was turned off for the purpose of tethering. - * - * Note that this is not part of the regular WIFI_ON setting because this only needs to - * be controlled through the settings app and not the Wi-Fi public API. - */ - private boolean testAndClearWifiSavedState() { - int wifiSavedState = getWifiSavedState(); - if (wifiSavedState == WIFI_ENABLED) { - setWifiSavedState(WIFI_DISABLED); - } - return (wifiSavedState == WIFI_ENABLED); - } - - /** - * Allow callers to set the Settings.Global.WIFI_SAVED_STATE property. - * - * When changing states, we need to remember what the wifi state was before switching. An - * example of this is when WiFiController switches to APEnabledState. Before swtiching to the - * new state, WifiController sets the current WiFi enabled/disabled state. When the AP is - * turned off, the WIFI_SAVED_STATE setting is used to restore the previous wifi state. - * - * @param state WiFi state to store with the Settings.Global.WIFI_SAVED_STATE property. - */ - public void setWifiSavedState(int state) { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_SAVED_STATE, state); - } - - /** - * Allow callers to get the Settings.Global.WIFI_SAVED_STATE property. - * - * When changing states we remember what the wifi state was before switching. This function is - * used to get the saved state. - * - * @return int Value for the previously saved state. - */ - public int getWifiSavedState() { - try { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.WIFI_SAVED_STATE); - } catch (Settings.SettingNotFoundException e) { - // If we have an error, return wifiSavedState off. - return WIFI_DISABLED; - } - } - private int getPersistedWifiState() { final ContentResolver cr = mContext.getContentResolver(); try { diff --git a/service/java/com/android/server/wifi/WifiShellCommand.java b/service/java/com/android/server/wifi/WifiShellCommand.java index b2289a8323..8a7bd75813 100644 --- a/service/java/com/android/server/wifi/WifiShellCommand.java +++ b/service/java/com/android/server/wifi/WifiShellCommand.java @@ -16,8 +16,6 @@ package com.android.server.wifi; -import android.app.AppGlobals; -import android.content.pm.IPackageManager; import android.net.wifi.WifiScanner; import android.os.Binder; import android.os.ShellCommand; @@ -50,20 +48,20 @@ public class WifiShellCommand extends ShellCommand { private final WifiLockManager mWifiLockManager; private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; private final WifiConfigManager mWifiConfigManager; - private final IPackageManager mPM; private final WifiNative mWifiNative; private final HostapdHal mHostapdHal; private final WifiCountryCode mWifiCountryCode; + private final WifiLastResortWatchdog mWifiLastResortWatchdog; WifiShellCommand(WifiInjector wifiInjector) { mClientModeImpl = wifiInjector.getClientModeImpl(); mWifiLockManager = wifiInjector.getWifiLockManager(); mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager(); mWifiConfigManager = wifiInjector.getWifiConfigManager(); - mPM = AppGlobals.getPackageManager(); mHostapdHal = wifiInjector.getHostapdHal(); mWifiNative = wifiInjector.getWifiNative(); mWifiCountryCode = wifiInjector.getWifiCountryCode(); + mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog(); } @Override @@ -245,6 +243,27 @@ public class WifiShellCommand extends ShellCommand { + mWifiCountryCode.getCountryCode()); return 0; } + case "set-wifi-watchdog": { + boolean enabled; + String nextArg = getNextArgRequired(); + if ("enabled".equals(nextArg)) { + enabled = true; + } else if ("disabled".equals(nextArg)) { + enabled = false; + } else { + pw.println( + "Invalid argument to 'set-wifi-watchdog' - must be 'enabled'" + + " or 'disabled'"); + return -1; + } + mWifiLastResortWatchdog.setWifiWatchdogFeature(enabled); + return 0; + } + case "get-wifi-watchdog": { + pw.println("wifi watchdog state is " + + mWifiLastResortWatchdog.getWifiWatchdogFeature()); + return 0; + } default: return handleDefaultCommands(cmd); } @@ -344,6 +363,10 @@ public class WifiShellCommand extends ShellCommand { pw.println(" Sets country code to <two-letter code> or left for normal value"); pw.println(" get-country-code"); pw.println(" Gets country code as a two-letter string"); + pw.println(" set-wifi-watchdog enabled|disabled"); + pw.println(" Sets whether wifi watchdog should trigger recovery"); + pw.println(" get-wifi-watchdog"); + pw.println(" Gets setting of wifi watchdog trigger recovery."); pw.println(); } } diff --git a/service/java/com/android/server/wifi/WifiStackService.java b/service/java/com/android/server/wifi/WifiStackService.java new file mode 100644 index 0000000000..214f85bca7 --- /dev/null +++ b/service/java/com/android/server/wifi/WifiStackService.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.notification.SystemNotificationChannels.NETWORK_ALERTS; +import static com.android.internal.notification.SystemNotificationChannels.NETWORK_AVAILABLE; +import static com.android.internal.notification.SystemNotificationChannels.NETWORK_STATUS; + +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.wifi.IWifiStackConnector; +import android.net.wifi.WifiApiServiceInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.UserHandle; +import android.os.storage.StorageManager; +import android.util.Log; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.server.wifi.aware.WifiAwareService; +import com.android.server.wifi.p2p.WifiP2pService; +import com.android.server.wifi.rtt.RttService; +import com.android.server.wifi.scanner.WifiScanningService; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Android service used to start the wifi stack when bound to via an intent. + */ +public class WifiStackService extends Service { + private static final String TAG = WifiStackService.class.getSimpleName(); + // Ordered list of wifi services. The ordering determines the order in which the events + // are delivered to the services. + @GuardedBy("mApiServices") + private final LinkedHashMap<String, WifiServiceBase> mApiServices = new LinkedHashMap<>(); + private static WifiStackConnector sConnector; + + private class WifiStackConnector extends IWifiStackConnector.Stub { + private final Context mContext; + + WifiStackConnector(Context context) { + mContext = context; + } + + @Override + public List<WifiApiServiceInfo> getWifiApiServiceInfos() { + // Ensure this is being invoked from system_server only. + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_STACK, "WifiStackService"); + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mApiServices) { + return mApiServices.entrySet().stream() + .map(entry -> { + WifiApiServiceInfo service = new WifiApiServiceInfo(); + service.name = entry.getKey(); + service.binder = entry.getValue().retrieveImpl(); + return service; + }) + .collect(Collectors.toList()); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + private class WifiBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.i(TAG, "Received " + intent + " broadcast"); + final String action = intent.getAction(); + if (action == null) { + Log.w(TAG, "Received null action for broadcast."); + return; + } + int userId; + switch (action) { + case Intent.ACTION_USER_SWITCHED: + userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + synchronized (mApiServices) { + for (WifiServiceBase service : mApiServices.values()) { + service.onSwitchUser(userId); + } + } + break; + case Intent.ACTION_USER_STOPPED: + userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + synchronized (mApiServices) { + for (WifiServiceBase service : mApiServices.values()) { + service.onStopUser(userId); + } + } + break; + case Intent.ACTION_USER_UNLOCKED: + userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + synchronized (mApiServices) { + for (WifiServiceBase service : mApiServices.values()) { + service.onUnlockUser(userId); + } + } + break; + default: + Log.e(TAG, "Received unexpected action for broadcast."); + break; + } + } + } + + // Create notification channels used by wifi. + private void createNotificationChannels() { + final NotificationManager nm = getSystemService(NotificationManager.class); + List<NotificationChannel> channelsList = new ArrayList<>(); + final NotificationChannel networkStatusChannel = new NotificationChannel( + NETWORK_STATUS, + getString(R.string.notification_channel_network_status), + NotificationManager.IMPORTANCE_LOW); + channelsList.add(networkStatusChannel); + + final NotificationChannel networkAlertsChannel = new NotificationChannel( + NETWORK_ALERTS, + getString(R.string.notification_channel_network_alerts), + NotificationManager.IMPORTANCE_HIGH); + networkAlertsChannel.setBlockableSystem(true); + channelsList.add(networkAlertsChannel); + + final NotificationChannel networkAvailable = new NotificationChannel( + NETWORK_AVAILABLE, + getString(R.string.notification_channel_network_available), + NotificationManager.IMPORTANCE_LOW); + networkAvailable.setBlockableSystem(true); + channelsList.add(networkAvailable); + + nm.createNotificationChannels(channelsList); + } + + + private synchronized boolean initializeServices(Context context) { + if (UserHandle.myUserId() != 0) { + Log.w(TAG, "Wifi stack can only be bound from primary user"); + return false; + } + // Don't start wifi services if we're in crypt bounce state. + if (StorageManager.inCryptKeeperBounce()) { + Log.d(TAG, "Device still encrypted. Need to restart SystemServer." + + " Do not start wifi."); + return false; + } + + // BootCompleteReceiver is registered in AndroidManifest.xml and here. The receiver + // registered here is triggered earlier, while the receiver registered in the manifest + // is more reliable since it is registered earlier, so we are guaranteed to get the + // broadcast (if we register too late the broadcast may have already triggered and we + // would have missed it). Register in both places and BootCompleteReceiver will ensure that + // callbacks are called exactly once. + Log.d(TAG, "Registering BootCompleteReceiver to listen for ACTION_LOCKED_BOOT_COMPLETED"); + context.registerReceiver(new BootCompleteReceiver(), + new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED)); + + synchronized (mApiServices) { + // Top level wifi feature flag. + if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { + Log.w(TAG, "Wifi not supported on the device"); + return false; + } + + // initialize static instance of WifiInjector + new WifiInjector(this); + // Ordering of wifi services. + // wifiscanner service + mApiServices.put(Context.WIFI_SCANNING_SERVICE, new WifiScanningService(this)); + // wifi service + mApiServices.put(Context.WIFI_SERVICE, new WifiService(this)); + // wifi-p2p service + if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) { + mApiServices.put(Context.WIFI_P2P_SERVICE, new WifiP2pService(this)); + } + // wifi-aware service + if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)) { + mApiServices.put(Context.WIFI_AWARE_SERVICE, new WifiAwareService(this)); + } + // wifirtt service + if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)) { + mApiServices.put(Context.WIFI_RTT_RANGING_SERVICE, new RttService(this)); + } + } + + Handler handler = new Handler(Looper.myLooper()); + // register callback to start Wifi services after boot completes + BootCompleteReceiver.registerCallback(() -> handler.post(() -> { + int currentUser = ActivityManager.getCurrentUser(); + synchronized (mApiServices) { + for (WifiServiceBase service : mApiServices.values()) { + service.onStart(); + // The current active user might have switched before the wifi services started + // up. So, send a onSwitchUser callback just after onStart callback is invoked. + if (currentUser != UserHandle.USER_SYSTEM) { + service.onSwitchUser(currentUser); + } + } + } + })); + + // Register broadcast receiver for system events. + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_USER_SWITCHED); + intentFilter.addAction(Intent.ACTION_USER_STOPPED); + intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); + registerReceiver(new WifiBroadcastReceiver(), intentFilter); + + // Create notification channels. + createNotificationChannels(); + + return true; + } + + /** + * Create a binder connector for the system server to communicate with the network stack. + * + * <p>On platforms where the network stack runs in the system server process, this method may + * be called directly instead of obtaining the connector by binding to the service. + */ + private synchronized IBinder makeConnectorAndInitializeServices(Context context) { + if (sConnector == null) { + if (!initializeServices(context)) { + Log.w(TAG, "Failed to initialize services"); + return null; + } + sConnector = new WifiStackConnector(context); + } + return sConnector; + } + + @NonNull + @Override + public IBinder onBind(Intent intent) { + Log.i(TAG, "WifiStack Service onBind"); + return makeConnectorAndInitializeServices(this); + } +} diff --git a/service/java/com/android/server/wifi/WifiThreadRunner.java b/service/java/com/android/server/wifi/WifiThreadRunner.java new file mode 100644 index 0000000000..3a0fd048ee --- /dev/null +++ b/service/java/com/android/server/wifi/WifiThreadRunner.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.os.Handler; +import android.os.Looper; +import android.os.SystemClock; +import android.util.Log; + +import com.android.server.wifi.util.GeneralUtil.Mutable; + +import java.util.function.Supplier; + +import javax.annotation.concurrent.ThreadSafe; + +/** + * Runs code on the main Wifi thread from another thread, in order to prevent race conditions. + */ +@ThreadSafe +public class WifiThreadRunner { + private static final String TAG = "WifiThreadRunner"; + + /** Max wait time for posting blocking runnables */ + private static final int RUN_WITH_SCISSORS_TIMEOUT_MILLIS = 4000; + + private final Handler mHandler; + + public WifiThreadRunner(Handler handler) { + mHandler = handler; + } + + /** + * Synchronously runs code on the main Wifi thread and return a value. + * <b>Blocks</b> the calling thread until the callable completes execution on the main Wifi + * thread. + * + * BEWARE OF DEADLOCKS!!! + * + * @param <T> the return type + * @param supplier the lambda that should be run on the main Wifi thread + * e.g. wifiThreadRunner.call(() -> mWifiApConfigStore.getApConfiguration()) + * or wifiThreadRunner.call(mWifiApConfigStore::getApConfiguration) + * @param valueToReturnOnTimeout If the lambda provided could not be run within the timeout ( + * {@link #RUN_WITH_SCISSORS_TIMEOUT_MILLIS}), will return this provided value + * instead. + * @return value retrieved from Wifi thread, or |valueToReturnOnTimeout| if the call failed. + * Beware of NullPointerExceptions when expecting a primitive (e.g. int, long) return + * type, it may still return null and throw a NullPointerException when auto-unboxing! + * Recommend capturing the return value in an Integer or Long instead and explicitly + * handling nulls. + */ + @Nullable + public <T> T call(@NonNull Supplier<T> supplier, T valueToReturnOnTimeout) { + Mutable<T> result = new Mutable<>(); + boolean runWithScissorsSuccess = runWithScissors(mHandler, + () -> result.value = supplier.get(), + RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + if (runWithScissorsSuccess) { + return result.value; + } else { + Log.e(TAG, "WifiThreadRunner.call() timed out!", new Throwable("Stack trace:")); + return valueToReturnOnTimeout; + } + } + + /** + * Runs a Runnable on the main Wifi thread and <b>blocks</b> the calling thread until the + * Runnable completes execution on the main Wifi thread. + * + * BEWARE OF DEADLOCKS!!! + * + * @return true if the runnable executed successfully, false otherwise + */ + public boolean run(@NonNull Runnable runnable) { + boolean runWithScissorsSuccess = + runWithScissors(mHandler, runnable, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); + if (runWithScissorsSuccess) { + return true; + } else { + Log.e(TAG, "WifiThreadRunner.run() timed out!", new Throwable("Stack trace:")); + return false; + } + } + + /** + * Asynchronously runs a Runnable on the main Wifi thread. + * + * @return true if the runnable was successfully posted <b>(not executed)</b> to the main Wifi + * thread, false otherwise + */ + public boolean post(@NonNull Runnable runnable) { + return mHandler.post(runnable); + } + + // Note: @hide methods copied from android.os.Handler + /** + * Runs the specified task synchronously. + * <p> + * If the current thread is the same as the handler thread, then the runnable + * runs immediately without being enqueued. Otherwise, posts the runnable + * to the handler and waits for it to complete before returning. + * </p><p> + * This method is dangerous! Improper use can result in deadlocks. + * Never call this method while any locks are held or use it in a + * possibly re-entrant manner. + * </p><p> + * This method is occasionally useful in situations where a background thread + * must synchronously await completion of a task that must run on the + * handler's thread. However, this problem is often a symptom of bad design. + * Consider improving the design (if possible) before resorting to this method. + * </p><p> + * One example of where you might want to use this method is when you just + * set up a Handler thread and need to perform some initialization steps on + * it before continuing execution. + * </p><p> + * If timeout occurs then this method returns <code>false</code> but the runnable + * will remain posted on the handler and may already be in progress or + * complete at a later time. + * </p><p> + * When using this method, be sure to use {@link Looper#quitSafely} when + * quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely. + * (TODO: We should fix this by making MessageQueue aware of blocking runnables.) + * </p> + * + * @param r The Runnable that will be executed synchronously. + * @param timeout The timeout in milliseconds, or 0 to wait indefinitely. + * + * @return Returns true if the Runnable was successfully executed. + * Returns false on failure, usually because the + * looper processing the message queue is exiting. + * + * @hide This method is prone to abuse and should probably not be in the API. + * If we ever do make it part of the API, we might want to rename it to something + * less funny like runUnsafe(). + */ + private static boolean runWithScissors(@NonNull Handler handler, @NonNull Runnable r, + long timeout) { + if (r == null) { + throw new IllegalArgumentException("runnable must not be null"); + } + if (timeout < 0) { + throw new IllegalArgumentException("timeout must be non-negative"); + } + + if (Looper.myLooper() == handler.getLooper()) { + r.run(); + return true; + } + + BlockingRunnable br = new BlockingRunnable(r); + return br.postAndWait(handler, timeout); + } + + private static final class BlockingRunnable implements Runnable { + private final Runnable mTask; + private boolean mDone; + + BlockingRunnable(Runnable task) { + mTask = task; + } + + @Override + public void run() { + try { + mTask.run(); + } finally { + synchronized (this) { + mDone = true; + notifyAll(); + } + } + } + + public boolean postAndWait(Handler handler, long timeout) { + if (!handler.post(this)) { + return false; + } + + synchronized (this) { + if (timeout > 0) { + final long expirationTime = SystemClock.uptimeMillis() + timeout; + while (!mDone) { + long delay = expirationTime - SystemClock.uptimeMillis(); + if (delay <= 0) { + return false; // timeout + } + try { + wait(delay); + } catch (InterruptedException ex) { + } + } + } else { + while (!mDone) { + try { + wait(); + } catch (InterruptedException ex) { + } + } + } + } + return true; + } + } +} diff --git a/service/java/com/android/server/wifi/WifiTrafficPoller.java b/service/java/com/android/server/wifi/WifiTrafficPoller.java index 90bdb13113..4491449e28 100644 --- a/service/java/com/android/server/wifi/WifiTrafficPoller.java +++ b/service/java/com/android/server/wifi/WifiTrafficPoller.java @@ -21,7 +21,6 @@ import android.net.wifi.ITrafficStateCallback; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -44,9 +43,8 @@ public class WifiTrafficPoller { private final ExternalCallbackTracker<ITrafficStateCallback> mRegisteredCallbacks; - WifiTrafficPoller(@NonNull Looper looper) { - mRegisteredCallbacks = new ExternalCallbackTracker<ITrafficStateCallback>( - new Handler(looper)); + WifiTrafficPoller(@NonNull Handler handler) { + mRegisteredCallbacks = new ExternalCallbackTracker<>(handler); } /** diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java index 464a34007b..a1d98061b3 100644 --- a/service/java/com/android/server/wifi/WifiVendorHal.java +++ b/service/java/com/android/server/wifi/WifiVendorHal.java @@ -53,7 +53,6 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.os.Handler; -import android.os.Looper; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; @@ -248,14 +247,11 @@ public class WifiVendorHal { // 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) { + public WifiVendorHal(HalDeviceManager halDeviceManager, Handler handler) { mHalDeviceManager = halDeviceManager; - mLooper = looper; - mHalEventHandler = new Handler(looper); + mHalEventHandler = handler; mHalDeviceManagerStatusCallbacks = new HalDeviceManagerStatusListener(); mIWifiStaIfaceEventCallback = new StaIfaceEventCallback(); mIWifiChipEventCallback = new ChipEventCallback(); @@ -1295,17 +1291,23 @@ public class WifiVendorHal { byte[] macByteArray = mac.toByteArray(); synchronized (sLock) { try { - android.hardware.wifi.V1_2.IWifiStaIface ifaceV12 = + android.hardware.wifi.V1_2.IWifiStaIface sta12 = getWifiStaIfaceForV1_2Mockable(ifaceName); - if (ifaceV12 == null) return boolResult(false); - WifiStatus status = ifaceV12.setMacAddress(macByteArray); - if (!ok(status)) return false; - return true; + if (sta12 != null) { + return ok(sta12.setMacAddress(macByteArray)); + } + + android.hardware.wifi.V1_4.IWifiApIface ap14 = + getWifiApIfaceForV1_4Mockable(ifaceName); + if (ap14 != null) { + return ok(ap14.setMacAddress(macByteArray)); + } } catch (RemoteException e) { handleRemoteException(e); return false; } } + return boolResult(false); } /** @@ -1320,20 +1322,33 @@ public class WifiVendorHal { } synchronized (sLock) { try { - android.hardware.wifi.V1_3.IWifiStaIface ifaceV13 = - getWifiStaIfaceForV1_3Mockable(ifaceName); - if (ifaceV13 == null) return null; AnswerBox box = new AnswerBox(); - ifaceV13.getFactoryMacAddress((status, macBytes) -> { - if (!ok(status)) return; - box.mac = MacAddress.fromBytes(macBytes); - }); - return box.mac; + + android.hardware.wifi.V1_3.IWifiStaIface sta13 = + getWifiStaIfaceForV1_3Mockable(ifaceName); + if (sta13 != null) { + sta13.getFactoryMacAddress((status, macBytes) -> { + if (!ok(status)) return; + box.mac = MacAddress.fromBytes(macBytes); + }); + return box.mac; + } + + android.hardware.wifi.V1_4.IWifiApIface ap14 = + getWifiApIfaceForV1_4Mockable(ifaceName); + if (ap14 != null) { + ap14.getFactoryMacAddress((status, macBytes) -> { + if (!ok(status)) return; + box.mac = MacAddress.fromBytes(macBytes); + }); + return box.mac; + } } catch (RemoteException e) { handleRemoteException(e); return null; } } + return null; } /** @@ -2311,6 +2326,13 @@ public class WifiVendorHal { return android.hardware.wifi.V1_3.IWifiStaIface.castFrom(iface); } + protected android.hardware.wifi.V1_4.IWifiApIface getWifiApIfaceForV1_4Mockable( + String ifaceName) { + IWifiApIface iface = getApIface(ifaceName); + if (iface == null) return null; + return android.hardware.wifi.V1_4.IWifiApIface.castFrom(iface); + } + /** * sarPowerBackoffRequired_1_1() * This method checks if we need to backoff wifi Tx power due to SAR requirements. diff --git a/service/java/com/android/server/wifi/WificondControl.java b/service/java/com/android/server/wifi/WificondControl.java index bd756f6439..0fc151bb85 100644 --- a/service/java/com/android/server/wifi/WificondControl.java +++ b/service/java/com/android/server/wifi/WificondControl.java @@ -20,21 +20,12 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; import android.annotation.NonNull; import android.app.AlarmManager; -import android.net.wifi.IApInterface; -import android.net.wifi.IApInterfaceEventCallback; -import android.net.wifi.IClientInterface; -import android.net.wifi.IPnoScanEvent; -import android.net.wifi.IScanEvent; -import android.net.wifi.ISendMgmtFrameEvent; -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.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -46,18 +37,30 @@ import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.ScanResultUtil; import com.android.server.wifi.wificond.ChannelSettings; import com.android.server.wifi.wificond.HiddenNetwork; +import com.android.server.wifi.wificond.IApInterface; +import com.android.server.wifi.wificond.IApInterfaceEventCallback; +import com.android.server.wifi.wificond.IClientInterface; +import com.android.server.wifi.wificond.IPnoScanEvent; +import com.android.server.wifi.wificond.IScanEvent; +import com.android.server.wifi.wificond.ISendMgmtFrameEvent; +import com.android.server.wifi.wificond.IWifiScannerImpl; +import com.android.server.wifi.wificond.IWificond; import com.android.server.wifi.wificond.NativeScanResult; +import com.android.server.wifi.wificond.NativeWifiClient; import com.android.server.wifi.wificond.PnoNetwork; import com.android.server.wifi.wificond.PnoSettings; import com.android.server.wifi.wificond.RadioChainInfo; import com.android.server.wifi.wificond.SingleScanSettings; import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; /** * This class provides methods for WifiNative to send control commands to wificond. @@ -129,13 +132,13 @@ public class WificondControl implements IBinder.DeathRecipient { } WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor, - CarrierNetworkConfig carrierNetworkConfig, AlarmManager alarmManager, Looper looper, + CarrierNetworkConfig carrierNetworkConfig, AlarmManager alarmManager, Handler handler, Clock clock) { mWifiInjector = wifiInjector; mWifiMonitor = wifiMonitor; mCarrierNetworkConfig = carrierNetworkConfig; mAlarmManager = alarmManager; - mEventHandler = new Handler(looper); + mEventHandler = handler; mClock = clock; } @@ -158,18 +161,6 @@ public class WificondControl implements IBinder.DeathRecipient { Log.d(TAG, "Pno Scan failed event"); mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount(); } - - @Override - public void OnPnoScanOverOffloadStarted() { - Log.d(TAG, "Pno scan over offload started"); - mWifiInjector.getWifiMetrics().incrementPnoScanStartedOverOffloadCount(); - } - - @Override - public void OnPnoScanOverOffloadFailed(int reason) { - Log.d(TAG, "Pno scan over offload failed"); - mWifiInjector.getWifiMetrics().incrementPnoScanFailedOverOffloadCount(); - } } /** @@ -183,8 +174,15 @@ public class WificondControl implements IBinder.DeathRecipient { } @Override - public void onNumAssociatedStationsChanged(int numStations) { - mSoftApListener.onNumAssociatedStationsChanged(numStations); + public void onConnectedClientsChanged(NativeWifiClient[] clients) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "onConnectedClientsChanged called with " + clients.length + " clients"); + for (int i = 0; i < clients.length; i++) { + Log.d(TAG, " mac " + clients[i].macAddress); + } + } + + mSoftApListener.onConnectedClientsChanged(Arrays.asList(clients)); } @Override @@ -385,6 +383,11 @@ public class WificondControl implements IBinder.DeathRecipient { return false; } + if (mWificond == null) { + Log.e(TAG, "Reference to wifiCond is null"); + return false; + } + boolean success; try { success = mWificond.tearDownClientInterface(ifaceName); @@ -444,6 +447,12 @@ public class WificondControl implements IBinder.DeathRecipient { Log.e(TAG, "No valid wificond ap interface handler"); return false; } + + if (mWificond == null) { + Log.e(TAG, "Reference to wifiCond is null"); + return false; + } + boolean success; try { success = mWificond.tearDownApInterface(ifaceName); @@ -559,6 +568,18 @@ public class WificondControl implements IBinder.DeathRecipient { return mWificondScanners.get(ifaceName); } + private static final int CAPABILITY_SIZE = 16; + + private static BitSet capabilityIntToBitset(int capabilityInt) { + BitSet capabilityBitSet = new BitSet(CAPABILITY_SIZE); + for (int i = 0; i < CAPABILITY_SIZE; i++) { + if ((capabilityInt & (1 << i)) != 0) { + capabilityBitSet.set(i); + } + } + return capabilityBitSet; + } + /** * Fetch the latest scan result from kernel via wificond. * @param ifaceName Name of the interface. @@ -596,7 +617,8 @@ public class WificondControl implements IBinder.DeathRecipient { InformationElementUtil.parseInformationElements(result.infoElement); InformationElementUtil.Capabilities capabilities = new InformationElementUtil.Capabilities(); - capabilities.from(ies, result.capability, isEnhancedOpenSupported()); + capabilities.from(ies, capabilityIntToBitset(result.capability), + isEnhancedOpenSupported()); String flags = capabilities.generateCapabilitiesString(); NetworkDetail networkDetail; try { @@ -607,7 +629,8 @@ public class WificondControl implements IBinder.DeathRecipient { } ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags, - result.signalMbm / 100, result.frequency, result.tsf, ies, null); + result.signalMbm / 100, result.frequency, result.tsf, ies, null, + result.infoElement); ScanResult scanResult = scanDetail.getScanResult(); // Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi // network and it uses EAP. @@ -622,7 +645,7 @@ public class WificondControl implements IBinder.DeathRecipient { // Fill up the radio chain info. if (result.radioChainInfos != null) { scanResult.radioChainInfos = - new ScanResult.RadioChainInfo[result.radioChainInfos.size()]; + new ScanResult.RadioChainInfo[result.radioChainInfos.length]; int idx = 0; for (RadioChainInfo nativeRadioChainInfo : result.radioChainInfos) { scanResult.radioChainInfos[idx] = new ScanResult.RadioChainInfo(); @@ -660,6 +683,33 @@ public class WificondControl implements IBinder.DeathRecipient { } /** + * HiddenNetwork is an AIDL generated classes, create a wrapper around it to implement a custom + * equals() method. + */ + private static final class HiddenNetworkWrapper { + HiddenNetwork mHiddenNetwork; + + HiddenNetworkWrapper(HiddenNetwork hiddenNetwork) { + mHiddenNetwork = hiddenNetwork; + } + + @Override + public boolean equals(Object o) { + if (o instanceof HiddenNetworkWrapper) { + HiddenNetworkWrapper that = (HiddenNetworkWrapper) o; + return Arrays.equals(this.mHiddenNetwork.ssid, that.mHiddenNetwork.ssid); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Arrays.hashCode(mHiddenNetwork.ssid); + } + } + + /** * Start a scan using wificond for the given parameters. * @param ifaceName Name of the interface. * @param scanType Type of scan to perform. @@ -683,31 +733,39 @@ public class WificondControl implements IBinder.DeathRecipient { Log.e(TAG, "Invalid scan type ", e); return false; } - 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 is expected to be very small, so this shouldn't cause - // any performance issues. - if (!settings.hiddenNetworks.contains(network)) { - settings.hiddenNetworks.add(network); - } - } + if (freqs == null) { + settings.channelSettings = new ChannelSettings[0]; + } else { + settings.channelSettings = freqs.stream() + .map(freq -> { + ChannelSettings channel = new ChannelSettings(); + channel.frequency = freq; + return channel; + }) + .toArray(ChannelSettings[]::new); + } + + if (hiddenNetworkSSIDs == null) { + settings.hiddenNetworks = new HiddenNetwork[0]; + } else { + settings.hiddenNetworks = hiddenNetworkSSIDs.stream() + .flatMap(ssid -> { + HiddenNetwork network = new HiddenNetwork(); + try { + network.ssid = NativeUtil.byteArrayFromArrayList( + NativeUtil.decodeSsid(ssid)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Illegal argument " + ssid, e); + return Stream.empty(); + } + // wrap so that we can use custom equals() method + HiddenNetworkWrapper wrapper = new HiddenNetworkWrapper(network); + return Stream.of(wrapper); + }) + .distinct() // use custom equals() method to dedupe + .map(wrapper -> wrapper.mHiddenNetwork) // unwrap + .toArray(HiddenNetwork[]::new); } try { @@ -731,25 +789,28 @@ public class WificondControl implements IBinder.DeathRecipient { 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; - } - condNetwork.frequencies = network.frequencies; - settings.pnoNetworks.add(condNetwork); - } + if (pnoSettings.networkList == null) { + settings.pnoNetworks = new PnoNetwork[0]; + } else { + settings.pnoNetworks = Arrays.stream(pnoSettings.networkList) + .flatMap(network -> { + 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); + return Stream.empty(); + } + condNetwork.frequencies = network.frequencies; + return Stream.of(condNetwork); + }) + .toArray(PnoNetwork[]::new); } try { diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java index 5832ee8981..5267c4c175 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java @@ -16,6 +16,8 @@ package com.android.server.wifi.aware; +import static android.net.RouteInfo.RTN_UNICAST; + import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; @@ -1532,7 +1534,8 @@ public class WifiAwareDataPathStateManager { linkProperties.setInterfaceName(nnri.interfaceName); linkProperties.addLinkAddress(new LinkAddress(linkLocal, 64)); linkProperties.addRoute( - new RouteInfo(new IpPrefix("fe80::/64"), null, nnri.interfaceName)); + new RouteInfo(new IpPrefix("fe80::/64"), null, nnri.interfaceName, + RTN_UNICAST)); return true; } diff --git a/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java b/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java index 2f072c06f0..b47f0a3091 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java @@ -234,7 +234,7 @@ public class WifiAwareMetrics { */ public void recordAttachStatus(int status) { synchronized (mLock) { - mAttachStatusData.put(status, mAttachStatusData.get(status) + 1); + addNanHalStatusToHistogram(status, mAttachStatusData); } } @@ -354,9 +354,9 @@ public class WifiAwareMetrics { public void recordDiscoveryStatus(int uid, int status, boolean isPublish) { synchronized (mLock) { if (isPublish) { - mPublishStatusData.put(status, mPublishStatusData.get(status) + 1); + addNanHalStatusToHistogram(status, mPublishStatusData); } else { - mSubscribeStatusData.put(status, mSubscribeStatusData.get(status) + 1); + addNanHalStatusToHistogram(status, mSubscribeStatusData); } if (status == NanStatusType.NO_RESOURCES_AVAILABLE) { @@ -463,9 +463,9 @@ public class WifiAwareMetrics { public void recordNdpStatus(int status, boolean isOutOfBand, long startTimestamp) { synchronized (mLock) { if (isOutOfBand) { - mOutOfBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1); + addNanHalStatusToHistogram(status, mOutOfBandNdpStatusData); } else { - mInBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1); + addNanHalStatusToHistogram(status, mInBandNdpStatusData); } if (status == NanStatusType.SUCCESS) { diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java index 366af13f35..7cb6e84c87 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java @@ -26,13 +26,16 @@ import android.hardware.wifi.V1_0.NanMatchInd; import android.hardware.wifi.V1_0.NanStatusType; import android.hardware.wifi.V1_0.WifiNanStatus; import android.hardware.wifi.V1_2.IWifiNanIfaceEventCallback; +import android.hardware.wifi.V1_2.NanDataPathChannelInfo; import android.hardware.wifi.V1_2.NanDataPathScheduleUpdateInd; import android.os.ShellCommand; import android.util.Log; +import android.util.SparseArray; import android.util.SparseIntArray; import libcore.util.HexEncoding; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -75,13 +78,14 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp private static final int CB_EV_DATA_PATH_SCHED_UPDATE = 11; private SparseIntArray mCallbackCounter = new SparseIntArray(); + private SparseArray<ArrayList<NanDataPathChannelInfo>> mChannelInfoPerNdp = new SparseArray<>(); private void incrementCbCount(int callbackId) { mCallbackCounter.put(callbackId, mCallbackCounter.get(callbackId) + 1); } /** - * Interpreter of adb shell command 'adb shell wifiaware native_cb ...'. + * Interpreter of adb shell command 'adb shell cmd wifiaware native_cb ...'. * * @return -1 if parameter not recognized or invalid value, 0 otherwise. */ @@ -121,6 +125,17 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp } return 0; } + case "get_channel_info": { + String option = parentShell.getNextOption(); + if (VDBG) Log.v(TAG, "option='" + option + "'"); + if (option != null) { + pwe.println("Unknown option to 'get_channel_info'"); + return -1; + } + String channelInfoString = convertChannelInfoToJsonString(); + pwo.println(channelInfoString); + return 0; + } default: pwe.println("Unknown 'wifiaware native_cb <cmd>'"); } @@ -140,6 +155,7 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp pw.println(" " + command); pw.println(" get_cb_count [--reset]: gets the number of callbacks (and optionally reset " + "count)"); + pw.println(" get_channel_info: prints out existing NDP channel info as a JSON String"); } @Override @@ -502,13 +518,15 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp + ", peerNdiMacAddr=" + String.valueOf( HexEncoding.encode(event.V1_0.peerNdiMacAddr)) + ", dataPathSetupSuccess=" + event.V1_0.dataPathSetupSuccess + ", reason=" + event.V1_0.status.status - + ", appInfo.size()=" + event.V1_0.appInfo.size()); + + ", appInfo.size()=" + event.V1_0.appInfo.size() + + ", channelInfo" + event.channelInfo); } if (!mIsHal12OrLater) { Log.wtf(TAG, "eventDataPathConfirm_1_2 should not be called by a <1.2 HAL!"); return; } incrementCbCount(CB_EV_DATA_PATH_CONFIRM); + mChannelInfoPerNdp.put(event.V1_0.ndpInstanceId, event.channelInfo); mWifiAwareStateManager.onDataPathConfirmNotification(event.V1_0.ndpInstanceId, event.V1_0.peerNdiMacAddr, event.V1_0.dataPathSetupSuccess, @@ -526,6 +544,9 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp return; } incrementCbCount(CB_EV_DATA_PATH_SCHED_UPDATE); + for (int ndpInstanceId : event.ndpInstanceIds) { + mChannelInfoPerNdp.put(ndpInstanceId, event.channelInfo); + } mWifiAwareStateManager.onDataPathScheduleUpdateNotification(event.peerDiscoveryAddress, event.ndpInstanceIds, event.channelInfo); @@ -535,16 +556,25 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp public void eventDataPathTerminated(int ndpInstanceId) { if (mDbg) Log.v(TAG, "eventDataPathTerminated: ndpInstanceId=" + ndpInstanceId); incrementCbCount(CB_EV_DATA_PATH_TERMINATED); + mChannelInfoPerNdp.remove(ndpInstanceId); mWifiAwareStateManager.onDataPathEndNotification(ndpInstanceId); } /** + * Reset the channel info when Aware is down. + */ + /* package */ void resetChannelInfo() { + mChannelInfoPerNdp.clear(); + } + + /** * Dump the internal state of the class. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("WifiAwareNativeCallback:"); pw.println(" mCallbackCounter: " + mCallbackCounter); + pw.println(" mChannelInfoPerNdp: " + mChannelInfoPerNdp); } @@ -577,4 +607,30 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp sb.append(status.status).append(" (").append(status.description).append(")"); return sb.toString(); } + + /** + * Transfer the channel Info dict into a Json String which can be decoded by Json reader. + * The Format is: "{ndpInstanceId: [{"channelFreq": channelFreq, + * "channelBandwidth": channelBandwidth, "numSpatialStreams": numSpatialStreams}]}" + * @return Json String. + */ + private String convertChannelInfoToJsonString() { + JSONObject channelInfoJson = new JSONObject(); + try { + for (int i = 0; i < mChannelInfoPerNdp.size(); i++) { + JSONArray infoJsonArray = new JSONArray(); + for (NanDataPathChannelInfo info : mChannelInfoPerNdp.valueAt(i)) { + JSONObject j = new JSONObject(); + j.put("channelFreq", info.channelFreq); + j.put("channelBandwidth", info.channelBandwidth); + j.put("numSpatialStreams", info.numSpatialStreams); + infoJsonArray.put(j); + } + channelInfoJson.put(Integer.toString(mChannelInfoPerNdp.keyAt(i)), infoJsonArray); + } + } catch (JSONException e) { + Log.e(TAG, "onCommand: get_channel_info e=" + e); + } + return channelInfoJson.toString(); + } } diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java index 6556bb084b..e3361bfc0a 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java @@ -51,6 +51,7 @@ public class WifiAwareNativeManager { private InterfaceAvailableForRequestListener mInterfaceAvailableForRequestListener = new InterfaceAvailableForRequestListener(); private int mReferenceCount = 0; + private volatile boolean mAwareNativeAvailable = false; WifiAwareNativeManager(WifiAwareStateManager awareStateManager, HalDeviceManager halDeviceManager, @@ -101,6 +102,13 @@ public class WifiAwareNativeManager { } /** + * Return the Availability of WifiAware native HAL + */ + public boolean isAwareNativeAvailable() { + return mAwareNativeAvailable; + } + + /** * Returns the native HAL WifiNanIface through which commands to the NAN HAL are dispatched. * Return may be null if not initialized/available. */ @@ -194,6 +202,7 @@ public class WifiAwareNativeManager { mInterfaceDestroyedListener = null; mHalDeviceManager.removeIface(mWifiNanIface); mWifiNanIface = null; + mWifiAwareNativeCallback.resetChannelInfo(); } } @@ -205,6 +214,7 @@ public class WifiAwareNativeManager { } mWifiNanIface = null; mReferenceCount = 0; + mAwareNativeAvailable = false; mWifiAwareStateManager.disableUsage(); } } @@ -235,8 +245,10 @@ public class WifiAwareNativeManager { } synchronized (mLock) { if (isAvailable) { + mAwareNativeAvailable = true; mWifiAwareStateManager.enableUsage(); } else if (mWifiNanIface == null) { // not available could mean already have NAN + mAwareNativeAvailable = false; mWifiAwareStateManager.disableUsage(); } } diff --git a/service/java/com/android/server/wifi/aware/WifiAwareService.java b/service/java/com/android/server/wifi/aware/WifiAwareService.java index f38c373163..e4b1e443c0 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareService.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareService.java @@ -17,64 +17,63 @@ package com.android.server.wifi.aware; import android.content.Context; +import android.os.Binder; 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; +import com.android.server.wifi.WifiServiceBase; /** * Service implementing Wi-Fi Aware functionality. Delegates actual interface * implementation to WifiAwareServiceImpl. */ -public final class WifiAwareService extends SystemService { +public final class WifiAwareService implements WifiServiceBase { 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); - } + Log.i(TAG, "Starting " + Context.WIFI_AWARE_SERVICE); + WifiInjector wifiInjector = WifiInjector.getInstance(); + if (wifiInjector == null) { + Log.e(TAG, "NULL injector!"); + return; + } - @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 halDeviceManager = wifiInjector.getHalDeviceManager(); + WifiAwareStateManager wifiAwareStateManager = new WifiAwareStateManager(); + WifiAwareNativeCallback wifiAwareNativeCallback = new WifiAwareNativeCallback( + wifiAwareStateManager); + WifiAwareNativeManager wifiAwareNativeManager = new WifiAwareNativeManager( + wifiAwareStateManager, halDeviceManager, wifiAwareNativeCallback); + WifiAwareNativeApi wifiAwareNativeApi = new WifiAwareNativeApi(wifiAwareNativeManager); + wifiAwareStateManager.setNative(wifiAwareNativeManager, wifiAwareNativeApi); + WifiAwareShellCommand wifiAwareShellCommand = new WifiAwareShellCommand(); + wifiAwareShellCommand.register("native_api", wifiAwareNativeApi); + wifiAwareShellCommand.register("native_cb", wifiAwareNativeCallback); + wifiAwareShellCommand.register("state_mgr", wifiAwareStateManager); - WifiAwareStateManager wifiAwareStateManager = new WifiAwareStateManager(); - WifiAwareNativeCallback wifiAwareNativeCallback = new WifiAwareNativeCallback( - wifiAwareStateManager); - WifiAwareNativeManager wifiAwareNativeManager = new WifiAwareNativeManager( - wifiAwareStateManager, halDeviceManager, wifiAwareNativeCallback); - WifiAwareNativeApi wifiAwareNativeApi = new WifiAwareNativeApi(wifiAwareNativeManager); - wifiAwareStateManager.setNative(wifiAwareNativeManager, wifiAwareNativeApi); - WifiAwareShellCommand wifiAwareShellCommand = new WifiAwareShellCommand(); - wifiAwareShellCommand.register("native_api", wifiAwareNativeApi); - wifiAwareShellCommand.register("native_cb", wifiAwareNativeCallback); - wifiAwareShellCommand.register("state_mgr", wifiAwareStateManager); + HandlerThread awareHandlerThread = wifiInjector.getWifiAwareHandlerThread(); + mImpl.start(awareHandlerThread, wifiAwareStateManager, wifiAwareShellCommand, + wifiInjector.getWifiMetrics().getWifiAwareMetrics(), + wifiInjector.getWifiPermissionsUtil(), + wifiInjector.getWifiPermissionsWrapper(), wifiInjector.getFrameworkFacade(), + wifiAwareNativeManager, wifiAwareNativeApi, wifiAwareNativeCallback); - HandlerThread awareHandlerThread = wifiInjector.getWifiAwareHandlerThread(); - mImpl.start(awareHandlerThread, wifiAwareStateManager, wifiAwareShellCommand, - wifiInjector.getWifiMetrics().getWifiAwareMetrics(), - wifiInjector.getWifiPermissionsUtil(), - wifiInjector.getWifiPermissionsWrapper(), wifiInjector.getFrameworkFacade(), - wifiAwareNativeManager, wifiAwareNativeApi, wifiAwareNativeCallback); - } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { - mImpl.startLate(); - } + // TODO: This 2 step initialization is no longer necessary because of service ordering in + // WifiStackService. + mImpl.startLate(); } -} + @Override + public Binder retrieveImpl() { + return mImpl; + } +} diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java index e6e961d690..a0f937d33b 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java @@ -418,7 +418,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { + ", retryCount=" + retryCount); } - mStateManager.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount); + mStateManager.sendMessage(uid, clientId, sessionId, peerId, message, messageId, retryCount); } @Override diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java index 9b096e525a..072788081b 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java @@ -673,8 +673,8 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe * 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) { + public void sendMessage(int uid, 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; @@ -683,6 +683,7 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe 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); + msg.getData().putInt(MESSAGE_BUNDLE_KEY_UID, uid); mSm.sendMessage(msg); } @@ -704,6 +705,10 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe if (mDbg) Log.d(TAG, "enableUsage(): while Wi-Fi is disabled - ignoring"); return; } + if (!mWifiAwareNativeManager.isAwareNativeAvailable()) { + if (mDbg) Log.d(TAG, "enableUsage(): while Aware Native isn't Available - ignoring"); + return; + } Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND); msg.arg1 = COMMAND_TYPE_ENABLE_USAGE; mSm.sendMessage(msg); @@ -1196,6 +1201,7 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe private short mCurrentTransactionId = TRANSACTION_ID_IGNORE; private static final long AWARE_SEND_MESSAGE_TIMEOUT = 10_000; + private static final int MESSAGE_QUEUE_DEPTH_PER_UID = 50; private int mSendArrivalSequenceCounter = 0; private boolean mSendQueueBlocked = false; private final SparseArray<Message> mHostQueuedSendMessages = new SparseArray<>(); @@ -1633,6 +1639,17 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe + msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID) + ", mSendArrivalSequenceCounter=" + mSendArrivalSequenceCounter); } + int uid = msg.getData().getInt(MESSAGE_BUNDLE_KEY_UID); + if (isUidExceededMessageQueueDepthLimit(uid)) { + if (mDbg) { + Log.v(TAG, "message queue limit exceeded for uid=" + uid + + " at messageId=" + + msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID)); + } + onMessageSendFailLocal(msg, NanStatusType.INTERNAL_FAILURE); + waitForResponse = false; + break; + } Message sendMsg = obtainMessage(msg.what); sendMsg.copyFrom(msg); sendMsg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ, @@ -2103,6 +2120,24 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe transmitNextMessage(); } + private boolean isUidExceededMessageQueueDepthLimit(int uid) { + int size = mHostQueuedSendMessages.size(); + int numOfMessages = 0; + if (size < MESSAGE_QUEUE_DEPTH_PER_UID) { + return false; + } + for (int i = 0; i < size; ++i) { + if (mHostQueuedSendMessages.valueAt(i).getData() + .getInt(MESSAGE_BUNDLE_KEY_UID) == uid) { + numOfMessages++; + if (numOfMessages >= MESSAGE_QUEUE_DEPTH_PER_UID) { + return true; + } + } + } + return false; + } + @Override protected String getLogRecString(Message msg) { StringBuilder sb = new StringBuilder(WifiAwareStateManager.messageToString(msg)); @@ -2542,6 +2577,10 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe } if (completedCommand.arg1 == COMMAND_TYPE_CONNECT) { + if (mCurrentAwareConfiguration == null) { // enabled (as opposed to re-configured) + createAllDataPathInterfaces(); + } + Bundle data = completedCommand.getData(); int clientId = completedCommand.arg2; @@ -2580,9 +2619,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe return; } - if (mCurrentAwareConfiguration == null) { // enabled (as opposed to re-configured) - createAllDataPathInterfaces(); - } mCurrentAwareConfiguration = mergeConfigRequests(null); if (mCurrentAwareConfiguration == null) { Log.wtf(TAG, "onConfigCompletedLocal: got a null merged configuration after config!?"); diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java index d95ab38312..825164bacc 100644 --- a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java +++ b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java @@ -292,7 +292,7 @@ public class ANQPMatcher { */ private static boolean matchMccMnc(String mccMnc, IMSIParameter imsiParam, List<String> simImsiList) { - if (imsiParam == null || simImsiList == null) { + if (imsiParam == null || simImsiList == null || mccMnc == null) { return false; } // Match against the IMSI parameter in the provider. diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java index edee2da1a1..36ed8592e0 100644 --- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java +++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java @@ -258,17 +258,28 @@ public class NetworkDetail { mANQPElements = null; //set up channel info mPrimaryFreq = freq; + int channelWidth = ScanResult.UNSPECIFIED; + int centerFreq0 = 0; + int centerFreq1 = 0; + + if (vhtOperation.isPresent()) { + channelWidth = vhtOperation.getChannelWidth(); + if (channelWidth != ScanResult.UNSPECIFIED) { + centerFreq0 = vhtOperation.getCenterFreq0(); + centerFreq1 = vhtOperation.getCenterFreq1(); + } + } - if (vhtOperation.isValid()) { - // 80 or 160 MHz - mChannelWidth = vhtOperation.getChannelWidth(); - mCenterfreq0 = vhtOperation.getCenterFreq0(); - mCenterfreq1 = vhtOperation.getCenterFreq1(); - } else { - mChannelWidth = htOperation.getChannelWidth(); - mCenterfreq0 = htOperation.getCenterFreq0(mPrimaryFreq); - mCenterfreq1 = 0; + if (channelWidth == ScanResult.UNSPECIFIED) { + //Either no vht, or vht shows BW is 40/20 MHz + if (htOperation.isPresent()) { + channelWidth = htOperation.getChannelWidth(); + centerFreq0 = htOperation.getCenterFreq0(mPrimaryFreq); + } } + mChannelWidth = channelWidth; + mCenterfreq0 = centerFreq0; + mCenterfreq1 = centerFreq1; // If trafficIndicationMap is not valid, mDtimPeriod will be negative if (trafficIndicationMap.isValid()) { @@ -287,8 +298,7 @@ public class NetworkDetail { maxRateA = supportedRates.mRates.get(supportedRates.mRates.size() - 1); mMaxRate = maxRateA > maxRateB ? maxRateA : maxRateB; mWifiMode = InformationElementUtil.WifiMode.determineMode(mPrimaryFreq, mMaxRate, - vhtOperation.isValid(), - iesFound.contains(ScanResult.InformationElement.EID_HT_OPERATION), + vhtOperation.isPresent(), htOperation.isPresent(), iesFound.contains(ScanResult.InformationElement.EID_ERP)); } else { mWifiMode = 0; @@ -303,9 +313,8 @@ public class NetworkDetail { + ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode) + ", Freq: " + mPrimaryFreq + ", mMaxRate: " + mMaxRate - + ", VHT: " + String.valueOf(vhtOperation.isValid()) - + ", HT: " + String.valueOf( - iesFound.contains(ScanResult.InformationElement.EID_HT_OPERATION)) + + ", VHT: " + String.valueOf(vhtOperation.isPresent()) + + ", HT: " + String.valueOf(htOperation.isPresent()) + ", ERP: " + String.valueOf( iesFound.contains(ScanResult.InformationElement.EID_ERP)) + ", SupportedRates: " + supportedRates.toString() diff --git a/service/java/com/android/server/wifi/hotspot2/OsuNetworkConnection.java b/service/java/com/android/server/wifi/hotspot2/OsuNetworkConnection.java index 08899ae283..b3bab6432e 100644 --- a/service/java/com/android/server/wifi/hotspot2/OsuNetworkConnection.java +++ b/service/java/com/android/server/wifi/hotspot2/OsuNetworkConnection.java @@ -28,6 +28,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; @@ -192,9 +193,12 @@ public class OsuNetworkConnection { if (TextUtils.isEmpty(nai)) { config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); } else { - // TODO: Handle OSEN. - Log.w(TAG, "OSEN not supported"); - return false; + // Setup OSEN connection with Unauthenticated user TLS and WFA Root certs + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OSEN); + config.allowedProtocols.set(WifiConfiguration.Protocol.OSEN); + config.enterpriseConfig.setDomainSuffixMatch(nai); + config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.UNAUTH_TLS); + config.enterpriseConfig.setCaPath(WfaKeyStore.DEFAULT_WFA_CERT_DIR); } mNetworkId = mWifiManager.addNetwork(config); if (mNetworkId < 0) { diff --git a/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java b/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java index 92560932c2..e096b144bf 100644 --- a/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java +++ b/service/java/com/android/server/wifi/hotspot2/OsuServerConnection.java @@ -411,11 +411,25 @@ public class OsuServerConnection { } X509Certificate certificate = getCert(certInfo.getKey()); - if (certificate == null || !ServiceProviderVerifier.verifyCertFingerprint( + if (certificate == null) { + // In case of an invalid cert, clear all of retrieved CA certs so that + // PasspointProvisioner aborts current flow. getCert already logs the error. + trustRootCertificates.clear(); + break; + } + + // Verify that the certificate's fingerprint matches the one provided in the PPS-MO + // profile, in accordance with section 7.3.1 of the HS2.0 specification. + if (!ServiceProviderVerifier.verifyCertFingerprint( certificate, certInfo.getValue())) { - // If any failure happens, clear all of retrieved CA certs so that + // If fingerprint does not match, clear all of retrieved CA certs so that // PasspointProvisioner aborts current flow. trustRootCertificates.clear(); + String certName = ""; + if (certificate.getSubjectDN() != null) { + certName = certificate.getSubjectDN().getName(); + } + Log.e(TAG, "Fingerprint does not match the certificate " + certName); break; } certificates.add(certificate); diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java index 0114cfb211..f2d7388a06 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java @@ -71,6 +71,7 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { "RemediationCaCertificateAlias"; private static final String XML_TAG_HAS_EVER_CONNECTED = "HasEverConnected"; + private static final String XML_TAG_IS_FROM_SUGGESTION = "IsFromSuggestion"; private final WifiKeyStore mKeyStore; private final SIMAccessor mSimAccessor; @@ -200,6 +201,7 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { XmlUtil.writeNextValue(out, XML_TAG_CLIENT_PRIVATE_KEY_ALIAS, provider.getClientPrivateKeyAlias()); XmlUtil.writeNextValue(out, XML_TAG_HAS_EVER_CONNECTED, provider.getHasEverConnected()); + XmlUtil.writeNextValue(out, XML_TAG_IS_FROM_SUGGESTION, provider.isFromSuggestion()); if (provider.getConfig() != null) { XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION); PasspointXmlUtils.serializePasspointConfiguration(out, provider.getConfig()); @@ -272,6 +274,7 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { String remediationCaCertificateAlias = null; String packageName = null; boolean hasEverConnected = false; + boolean isFromSuggestion = false; boolean shared = false; PasspointConfiguration config = null; while (XmlUtils.nextElementWithin(in, outerTagDepth)) { @@ -309,6 +312,9 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { case XML_TAG_HAS_EVER_CONNECTED: hasEverConnected = (boolean) value; break; + case XML_TAG_IS_FROM_SUGGESTION: + isFromSuggestion = (boolean) value; + break; } } else { if (!TextUtils.equals(in.getName(), @@ -338,8 +344,8 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { throw new XmlPullParserException("Missing Passpoint configuration"); } return new PasspointProvider(config, mKeyStore, mSimAccessor, providerId, creatorUid, - packageName, caCertificateAliases, clientCertificateAlias, clientPrivateKeyAlias, - remediationCaCertificateAlias, hasEverConnected, shared); + packageName, isFromSuggestion, caCertificateAliases, clientCertificateAlias, + clientPrivateKeyAlias, remediationCaCertificateAlias, hasEverConnected, shared); } } diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index c468737613..af4a6c883d 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -98,7 +98,7 @@ import java.util.stream.Collectors; * 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 ClientModeImpl thread. + * NOTE: These API's are not thread safe and should only be used from the main Wifi thread. */ public class PasspointManager { private static final String TAG = "PasspointManager"; @@ -364,7 +364,9 @@ public class PasspointManager { } /** - * Initializes the provisioning flow with a looper + * Initializes the provisioning flow with a looper. + * This looper should be tied to a background worker thread since PasspointProvisioner has a + * heavy workload. */ public void initializeProvisioner(Looper looper) { mPasspointProvisioner.init(looper); @@ -390,7 +392,8 @@ public class PasspointManager { * @param packageName Package name of the app adding/Updating {@code config} * @return true if provider is added, false otherwise */ - public boolean addOrUpdateProvider(PasspointConfiguration config, int uid, String packageName) { + public boolean addOrUpdateProvider(PasspointConfiguration config, int uid, + String packageName, boolean isFromSuggestion) { mWifiMetrics.incrementNumPasspointProviderInstallation(); if (config == null) { Log.e(TAG, "Configuration not provided"); @@ -419,8 +422,8 @@ public class PasspointManager { } // Create a provider and install the necessary certificates and keys. - PasspointProvider newProvider = mObjectFactory.makePasspointProvider( - config, mKeyStore, mSimAccessor, mProviderIndex++, uid, packageName); + PasspointProvider newProvider = mObjectFactory.makePasspointProvider(config, mKeyStore, + mSimAccessor, mProviderIndex++, uid, packageName, isFromSuggestion); if (!newProvider.installCertsAndKeys()) { Log.e(TAG, "Failed to install certificates and keys to keystore"); @@ -429,13 +432,26 @@ public class PasspointManager { // Remove existing provider with the same FQDN. if (mProviders.containsKey(config.getHomeSp().getFqdn())) { + PasspointProvider old = mProviders.get(config.getHomeSp().getFqdn()); + // If new profile is from suggestion and from a different App, ignore new profile, + // return true. + // If from same app, update it. + if (isFromSuggestion && !old.getPackageName().equals(packageName)) { + newProvider.uninstallCertsAndKeys(); + return false; + } Log.d(TAG, "Replacing configuration for " + config.getHomeSp().getFqdn()); - mProviders.get(config.getHomeSp().getFqdn()).uninstallCertsAndKeys(); + old.uninstallCertsAndKeys(); mProviders.remove(config.getHomeSp().getFqdn()); + // New profile changes the credential, remove the related WifiConfig. + if (!old.equals(newProvider)) { + mWifiConfigManager.removePasspointConfiguredNetwork( + newProvider.getWifiConfig().configKey()); + } } mProviders.put(config.getHomeSp().getFqdn(), newProvider); mWifiConfigManager.saveToStore(true /* forceWrite */); - if (newProvider.getPackageName() != null) { + if (!isFromSuggestion && newProvider.getPackageName() != null) { startTrackingAppOpsChange(newProvider.getPackageName(), uid); } Log.d(TAG, "Added/updated Passpoint configuration: " + config.getHomeSp().getFqdn() @@ -646,7 +662,7 @@ public class PasspointManager { // Create a provider and install the necessary certificates and keys. PasspointProvider newProvider = mObjectFactory.makePasspointProvider( - config, mKeyStore, mSimAccessor, mProviderIndex++, Process.WIFI_UID, null); + config, mKeyStore, mSimAccessor, mProviderIndex++, Process.WIFI_UID, null, false); newProvider.setEphemeral(true); Log.d(TAG, "installed PasspointConfiguration for carrier : " + config.getHomeSp().getFriendlyName()); @@ -665,7 +681,6 @@ public class PasspointManager { */ public boolean removeProvider(int callingUid, boolean privileged, String fqdn) { mWifiMetrics.incrementNumPasspointProviderUninstallation(); - String packageName; PasspointProvider provider = mProviders.get(fqdn); if (provider == null) { Log.e(TAG, "Config doesn't exist"); @@ -677,7 +692,10 @@ public class PasspointManager { return false; } provider.uninstallCertsAndKeys(); - packageName = provider.getPackageName(); + String packageName = provider.getPackageName(); + // Remove any configs corresponding to the profile in WifiConfigManager. + mWifiConfigManager.removePasspointConfiguredNetwork( + provider.getWifiConfig().configKey()); mProviders.remove(fqdn); mWifiConfigManager.saveToStore(true /* forceWrite */); @@ -707,18 +725,21 @@ public class PasspointManager { /** * Return the installed Passpoint provider configurations. - * * An empty list will be returned when no provider is installed. * * @param callingUid Calling UID. * @param privileged Whether the caller is a privileged entity * @return A list of {@link PasspointConfiguration} */ - public List<PasspointConfiguration> getProviderConfigs(int callingUid, boolean privileged) { + public List<PasspointConfiguration> getProviderConfigs(int callingUid, + boolean privileged) { List<PasspointConfiguration> configs = new ArrayList<>(); for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { PasspointProvider provider = entry.getValue(); if (privileged || callingUid == provider.getCreatorUid()) { + if (provider.isEphemeral() || provider.isFromSuggestion()) { + continue; + } configs.add(provider.getConfig()); } } @@ -746,12 +767,14 @@ public class PasspointManager { } Pair<PasspointProvider, PasspointMatch> bestMatch = null; for (Pair<PasspointProvider, PasspointMatch> match : allMatches) { - if (match.second == PasspointMatch.HomeProvider) { - bestMatch = match; - break; - } - if (match.second == PasspointMatch.RoamingProvider && bestMatch == null) { - bestMatch = match; + if (!isExpired(match.first.getConfig())) { + if (match.second == PasspointMatch.HomeProvider) { + bestMatch = match; + break; + } + if (match.second == PasspointMatch.RoamingProvider && bestMatch == null) { + bestMatch = match; + } } } if (bestMatch != null) { @@ -808,6 +831,15 @@ public class PasspointManager { roamingConsortium); if (matchStatus == PasspointMatch.HomeProvider || matchStatus == PasspointMatch.RoamingProvider) { + // If provider is from network suggestion, check user approval. + // Send user approval notification if need. + // If not approved, will be ignored in this matching. + if (provider.isFromSuggestion() + && mWifiInjector.getWifiNetworkSuggestionsManager() + .sendUserApprovalNotificationIfNotApproved( + provider.getPackageName(), provider.getCreatorUid())) { + continue; + } allMatches.add(Pair.create(provider, matchStatus)); } } @@ -1020,13 +1052,12 @@ public class PasspointManager { public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders( List<OsuProvider> osuProviders) { Map<OsuProvider, PasspointConfiguration> matchingPasspointConfigs = new HashMap<>(); - List<PasspointConfiguration> passpointConfigurations = - getProviderConfigs(Process.WIFI_UID /* ignored */, true); for (OsuProvider osuProvider : osuProviders) { Map<String, String> friendlyNamesForOsuProvider = osuProvider.getFriendlyNameList(); if (friendlyNamesForOsuProvider == null) continue; - for (PasspointConfiguration passpointConfiguration : passpointConfigurations) { + for (PasspointProvider provider : mProviders.values()) { + PasspointConfiguration passpointConfiguration = provider.getConfig(); Map<String, String> serviceFriendlyNamesForPpsMo = passpointConfiguration.getServiceFriendlyNames(); if (serviceFriendlyNamesForPpsMo == null) continue; @@ -1161,7 +1192,7 @@ public class PasspointManager { // 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, null, + mSimAccessor, mProviderIndex++, wifiConfig.creatorUid, null, false, Arrays.asList(enterpriseConfig.getCaCertificateAlias()), enterpriseConfig.getClientCertificateAlias(), enterpriseConfig.getClientCertificateAlias(), null, false, false); @@ -1180,4 +1211,28 @@ public class PasspointManager { IProvisioningCallback callback) { return mPasspointProvisioner.startSubscriptionProvisioning(callingUid, provider, callback); } + + /** + * Check if a Passpoint configuration is expired + * + * @param config {@link PasspointConfiguration} Passpoint configuration + * @return True if the configuration is expired, false if not or expiration is unset + */ + private boolean isExpired(@NonNull PasspointConfiguration config) { + long expirationTime = config.getSubscriptionExpirationTimeInMillis(); + + if (expirationTime != Long.MIN_VALUE) { + long curTime = System.currentTimeMillis(); + + // Check expiration and return true for expired profiles + if (curTime >= expirationTime) { + Log.d(TAG, "Profile for " + config.getServiceFriendlyName() + " has expired, " + + "expiration time: " + expirationTime + ", current time: " + + curTime); + return true; + } + } + + return false; + } } diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java index ec8a009d98..2d98a9d719 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java @@ -244,13 +244,18 @@ public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEva } // Add the newly created WifiConfiguration to WifiConfigManager. - NetworkUpdateResult result = - mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); + NetworkUpdateResult result; + if (config.fromWifiNetworkSuggestion) { + result = mWifiConfigManager.addOrUpdateNetwork( + config, config.creatorUid, config.creatorName); + } else { + 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.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID, null); mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(), networkInfo.mScanDetail.getScanResult(), 0); mWifiConfigManager.updateScanDetailForNetwork( diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java index c083b86cc6..94be270e2b 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java @@ -59,9 +59,9 @@ public class PasspointObjectFactory{ */ public PasspointProvider makePasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, SIMAccessor simAccessor, long providerId, int creatorUid, - String packageName) { + String packageName, boolean isFromSuggestion) { return new PasspointProvider(config, keyStore, simAccessor, providerId, creatorUid, - packageName); + packageName, isFromSuggestion); } /** diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java index ca9814aa6a..70ea738ac5 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java @@ -30,6 +30,7 @@ import android.text.TextUtils; import android.util.Base64; import android.util.Log; +import com.android.internal.util.ArrayUtils; import com.android.server.wifi.IMSIParameter; import com.android.server.wifi.SIMAccessor; import com.android.server.wifi.WifiKeyStore; @@ -69,6 +70,8 @@ public class PasspointProvider { private static final String ALIAS_HS_TYPE = "HS2_"; private static final String ALIAS_ALIAS_REMEDIATION_TYPE = "REMEDIATION_"; + private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts"; + private final PasspointConfiguration mConfig; private final WifiKeyStore mKeyStore; @@ -95,6 +98,8 @@ public class PasspointProvider { private boolean mHasEverConnected; private boolean mIsShared; + private boolean mIsFromSuggestion; + /** * This is a flag to indicate if the Provider is created temporarily. @@ -103,14 +108,15 @@ public class PasspointProvider { private boolean mIsEphemeral = false; public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, - SIMAccessor simAccessor, long providerId, int creatorUid, String packageName) { - this(config, keyStore, simAccessor, providerId, creatorUid, packageName, null, null, null, - null, false, false); + SIMAccessor simAccessor, long providerId, int creatorUid, String packageName, + boolean isFromSuggestion) { + this(config, keyStore, simAccessor, providerId, creatorUid, packageName, isFromSuggestion, + null, null, null, null, false, false); } public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, SIMAccessor simAccessor, long providerId, int creatorUid, String packageName, - List<String> caCertificateAliases, + boolean isFromSuggestion, List<String> caCertificateAliases, String clientCertificateAlias, String clientPrivateKeyAlias, String remediationCaCertificateAlias, boolean hasEverConnected, boolean isShared) { @@ -126,6 +132,7 @@ public class PasspointProvider { mRemediationCaCertificateAlias = remediationCaCertificateAlias; mHasEverConnected = hasEverConnected; mIsShared = isShared; + mIsFromSuggestion = isFromSuggestion; // Setup EAP method and authentication parameter based on the credential. if (mConfig.getCredential().getUserCredential() != null) { @@ -202,6 +209,10 @@ public class PasspointProvider { return mImsiParameter; } + public boolean isFromSuggestion() { + return mIsFromSuggestion; + } + /** * Install certificates and key based on current configuration. * Note: the certificates and keys in the configuration will get cleared once @@ -407,8 +418,26 @@ public class PasspointProvider { buildEnterpriseConfigForSimCredential(enterpriseConfig, mConfig.getCredential().getSimCredential()); } + // If AAA server trusted names are specified, use it to replace HOME SP FQDN + // and use system CA regardless of provisioned CA certificate. + if (!ArrayUtils.isEmpty(mConfig.getAaaServerTrustedNames())) { + enterpriseConfig.setDomainSuffixMatch( + String.join(";", mConfig.getAaaServerTrustedNames())); + enterpriseConfig.setCaCertificateAliases(new String[] {SYSTEM_CA_STORE_PATH}); + } wifiConfig.enterpriseConfig = enterpriseConfig; + // PPS MO Credential/CheckAAAServerCertStatus node contains a flag which indicates + // if the mobile device needs to check the AAA server certificate's revocation status + // during EAP authentication. + if (mConfig.getCredential().getCheckAaaServerCertStatus()) { + // Check server certificate using OCSP (Online Certificate Status Protocol). + wifiConfig.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); + } wifiConfig.shared = mIsShared; + wifiConfig.fromWifiNetworkSuggestion = mIsFromSuggestion; + wifiConfig.ephemeral = mIsFromSuggestion; + wifiConfig.creatorName = mPackageName; + wifiConfig.creatorUid = mCreatorUid; return wifiConfig; } @@ -519,6 +548,9 @@ public class PasspointProvider { builder.append("Configuration Begin ---\n"); builder.append(mConfig); builder.append("Configuration End ---\n"); + builder.append("WifiConfiguration Begin ---\n"); + builder.append(getWifiConfig()); + builder.append("WifiConfiguration End ---\n"); return builder.toString(); } @@ -622,7 +654,11 @@ public class PasspointProvider { config.setEapMethod(WifiEnterpriseConfig.Eap.TTLS); config.setIdentity(credential.getUsername()); config.setPassword(decodedPassword); - config.setCaCertificateAliases(mCaCertificateAliases.toArray(new String[0])); + if (!ArrayUtils.isEmpty(mCaCertificateAliases)) { + config.setCaCertificateAliases(mCaCertificateAliases.toArray(new String[0])); + } else { + config.setCaCertificateAliases(new String[] {SYSTEM_CA_STORE_PATH}); + } int phase2Method = WifiEnterpriseConfig.Phase2.NONE; switch (credential.getNonEapInnerMethod()) { case Credential.UserCredential.AUTH_METHOD_PAP: @@ -651,7 +687,11 @@ public class PasspointProvider { private void buildEnterpriseConfigForCertCredential(WifiEnterpriseConfig config) { config.setEapMethod(WifiEnterpriseConfig.Eap.TLS); config.setClientCertificateAlias(mClientCertificateAlias); - config.setCaCertificateAliases(mCaCertificateAliases.toArray(new String[0])); + if (!ArrayUtils.isEmpty(mCaCertificateAliases)) { + config.setCaCertificateAliases(mCaCertificateAliases.toArray(new String[0])); + } else { + config.setCaCertificateAliases(new String[] {SYSTEM_CA_STORE_PATH}); + } } /** diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java index 137d9fa19c..b9afb0bac2 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java @@ -789,7 +789,8 @@ public class PasspointProvisioner { // Verify that the intent will resolve to an activity if (intent.resolveActivity(mContext.getPackageManager()) != null) { - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + // TODO (b/142234604): This will not work on multi-user device scenarios. + mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); invokeProvisioningCallback(PROVISIONING_STATUS, ProvisioningCallback.OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE); changeState(STATE_WAITING_FOR_REDIRECT_RESPONSE); diff --git a/service/java/com/android/server/wifi/hotspot2/ServiceProviderVerifier.java b/service/java/com/android/server/wifi/hotspot2/ServiceProviderVerifier.java index d0f6dd5410..bae7b70194 100644 --- a/service/java/com/android/server/wifi/hotspot2/ServiceProviderVerifier.java +++ b/service/java/com/android/server/wifi/hotspot2/ServiceProviderVerifier.java @@ -22,12 +22,13 @@ import android.util.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; -import com.android.org.bouncycastle.asn1.ASN1Encodable; -import com.android.org.bouncycastle.asn1.ASN1InputStream; -import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; -import com.android.org.bouncycastle.asn1.ASN1Sequence; -import com.android.org.bouncycastle.asn1.DERTaggedObject; -import com.android.org.bouncycastle.asn1.DERUTF8String; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DERUTF8String; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; diff --git a/service/java/com/android/server/wifi/hotspot2/WfaKeyStore.java b/service/java/com/android/server/wifi/hotspot2/WfaKeyStore.java index 342ce1cd88..c85f7f9639 100644 --- a/service/java/com/android/server/wifi/hotspot2/WfaKeyStore.java +++ b/service/java/com/android/server/wifi/hotspot2/WfaKeyStore.java @@ -34,7 +34,7 @@ public class WfaKeyStore { private static final String TAG = "PasspointWfaKeyStore"; // The WFA Root certs are checked in to /system/ca-certificates/cacerts_wfa // The location on device is configured in the corresponding Android.mk - private static final String DEFAULT_WFA_CERT_DIR = + /* package */ static final String DEFAULT_WFA_CERT_DIR = Environment.getRootDirectory() + "/etc/security/cacerts_wfa"; private boolean mVerboseLoggingEnabled = false; diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pService.java b/service/java/com/android/server/wifi/p2p/WifiP2pService.java index 6137c3cd95..97da28b3e9 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pService.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pService.java @@ -17,36 +17,34 @@ package com.android.server.wifi.p2p; import android.content.Context; +import android.os.Binder; import android.util.Log; -import com.android.server.SystemService; import com.android.server.wifi.WifiInjector; +import com.android.server.wifi.WifiServiceBase; /** * Wifi P2p Service class, instantiates P2p service - * Overrides onStart() and onBootPhase() methods in + * Overrides onStart() and onBootCompleted() methods in * the super class. */ -public final class WifiP2pService extends SystemService { +public final class WifiP2pService implements WifiServiceBase { private static final String TAG = "WifiP2pService"; final WifiP2pServiceImpl mImpl; public WifiP2pService(Context context) { - super(context); mImpl = new WifiP2pServiceImpl(context, WifiInjector.getInstance()); } @Override public void onStart() { - Log.i(TAG, "Registering " + Context.WIFI_P2P_SERVICE); - publishBinderService(Context.WIFI_P2P_SERVICE, mImpl); + Log.i(TAG, "Starting " + Context.WIFI_P2P_SERVICE); + mImpl.connectivityServiceReady(); } @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - mImpl.connectivityServiceReady(); - } + public Binder retrieveImpl() { + return mImpl; } } diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java index 3f37456641..dfbc45f8ac 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java @@ -72,7 +72,6 @@ import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import android.util.Slog; import android.util.SparseArray; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -397,7 +396,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mP2pStateMachine.sendMessage(Message.obtain(msg)); break; default: - Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg); + Log.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg); break; } } @@ -462,6 +461,25 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } /** + * If wifi p2p interface name pattern is defined, + * {@link com.android.server.connectivity.Tethering} listens to + * {@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} + * events and takes over the DHCP server management automatically. + */ + private boolean isDhcpServerHostedByDnsmasq() { + try { + String[] tetherableWifiP2pRegexs = mContext.getResources().getStringArray( + com.android.internal.R.array.config_tether_wifi_p2p_regexs); + return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0); + } catch (Resources.NotFoundException e404) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "No P2P tetherable interface pattern"); + } + } + return true; + } + + /** * Obtains the service interface for Managements services */ public void connectivityServiceReady() { @@ -2490,9 +2508,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { 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.getWindow().addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); dialog.show(); mFrequencyConflictDialog = dialog; } @@ -3017,6 +3034,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } private void startDhcpServer(String intf) { + if (!isDhcpServerHostedByDnsmasq()) return; + InterfaceConfiguration ifcg = null; try { ifcg = mNwService.getInterfaceConfig(intf); @@ -3043,6 +3062,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } private void stopDhcpServer(String intf) { + if (!isDhcpServerHostedByDnsmasq()) return; + try { mNwService.untetherInterface(intf); for (String temp : mNwService.listTetheredInterfaces()) { @@ -3070,9 +3091,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { .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.getWindow().addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); dialog.show(); } @@ -3102,9 +3122,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { .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.getWindow().addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); dialog.show(); } @@ -3128,9 +3147,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { .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.getWindow().addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); dialog.show(); } @@ -3212,9 +3230,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } 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.getWindow().addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); dialog.show(); } @@ -3784,12 +3801,12 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { @Override protected void logd(String s) { - Slog.d(TAG, s); + Log.d(TAG, s); } @Override protected void loge(String s) { - Slog.e(TAG, s); + Log.e(TAG, s); } /** diff --git a/service/java/com/android/server/wifi/rtt/RttService.java b/service/java/com/android/server/wifi/rtt/RttService.java index 74370516e2..e4ce9235fd 100644 --- a/service/java/com/android/server/wifi/rtt/RttService.java +++ b/service/java/com/android/server/wifi/rtt/RttService.java @@ -18,57 +18,53 @@ package com.android.server.wifi.rtt; import android.content.Context; import android.net.wifi.aware.IWifiAwareManager; +import android.os.Binder; import android.os.HandlerThread; import android.os.ServiceManager; import android.util.Log; -import com.android.server.SystemService; import com.android.server.wifi.HalDeviceManager; import com.android.server.wifi.WifiInjector; +import com.android.server.wifi.WifiServiceBase; import com.android.server.wifi.util.WifiPermissionsUtil; /** * TBD. */ -public class RttService extends SystemService { +public class RttService implements WifiServiceBase { private static final String TAG = "RttService"; private Context mContext; private RttServiceImpl mImpl; public RttService(Context context) { - super(context); mContext = context; mImpl = new RttServiceImpl(context); } @Override public void onStart() { - Log.i(TAG, "Registering " + Context.WIFI_RTT_RANGING_SERVICE); - publishBinderService(Context.WIFI_RTT_RANGING_SERVICE, mImpl); - } - - @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - Log.i(TAG, "Starting " + Context.WIFI_RTT_RANGING_SERVICE); + Log.i(TAG, "Starting " + Context.WIFI_RTT_RANGING_SERVICE); + WifiInjector wifiInjector = WifiInjector.getInstance(); + if (wifiInjector == null) { + Log.e(TAG, "onBootPhase(PHASE_SYSTEM_SERVICES_READY): NULL injector!"); + return; + } - WifiInjector wifiInjector = WifiInjector.getInstance(); - if (wifiInjector == null) { - Log.e(TAG, "onBootPhase(PHASE_SYSTEM_SERVICES_READY): NULL injector!"); - return; - } + HalDeviceManager halDeviceManager = wifiInjector.getHalDeviceManager(); + HandlerThread handlerThread = wifiInjector.getRttHandlerThread(); + WifiPermissionsUtil wifiPermissionsUtil = wifiInjector.getWifiPermissionsUtil(); + RttMetrics rttMetrics = wifiInjector.getWifiMetrics().getRttMetrics(); - HalDeviceManager halDeviceManager = wifiInjector.getHalDeviceManager(); - HandlerThread handlerThread = wifiInjector.getRttHandlerThread(); - WifiPermissionsUtil wifiPermissionsUtil = wifiInjector.getWifiPermissionsUtil(); - RttMetrics rttMetrics = wifiInjector.getWifiMetrics().getRttMetrics(); + IWifiAwareManager awareBinder = (IWifiAwareManager) ServiceManager.getService( + Context.WIFI_AWARE_SERVICE); - IWifiAwareManager awareBinder = (IWifiAwareManager) ServiceManager.getService( - Context.WIFI_AWARE_SERVICE); + RttNative rttNative = new RttNative(mImpl, halDeviceManager); + mImpl.start(handlerThread.getLooper(), wifiInjector.getClock(), awareBinder, rttNative, + rttMetrics, wifiPermissionsUtil, wifiInjector.getFrameworkFacade()); + } - RttNative rttNative = new RttNative(mImpl, halDeviceManager); - mImpl.start(handlerThread.getLooper(), wifiInjector.getClock(), awareBinder, rttNative, - rttMetrics, wifiPermissionsUtil, wifiInjector.getFrameworkFacade()); - } + @Override + public Binder retrieveImpl() { + return mImpl; } } diff --git a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java index d69ce8f00a..ce64d2e4a6 100644 --- a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java +++ b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java @@ -399,7 +399,7 @@ public class RttServiceImpl extends IWifiRttManager.Stub { public boolean isAvailable() { long ident = Binder.clearCallingIdentity(); try { - return mRttNative.isReady() && !mPowerManager.isDeviceIdleMode() + return mRttNative != null && mRttNative.isReady() && !mPowerManager.isDeviceIdleMode() && mWifiPermissionsUtil.isLocationModeEnabled(); } finally { Binder.restoreCallingIdentity(ident); diff --git a/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java b/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java index 01d64e9f51..2749aedf37 100644 --- a/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java +++ b/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java @@ -23,9 +23,9 @@ import android.net.wifi.WifiScanner; import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiScanner.ScanSettings; import android.util.ArraySet; +import android.util.Log; import android.util.Pair; import android.util.Rational; -import android.util.Slog; import com.android.server.wifi.WifiNative; import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; @@ -425,7 +425,7 @@ public class BackgroundScanScheduler { if (maxScheduledBucket != null) { return maxScheduledBucket.bucketId; } else { - Slog.wtf(TAG, "No bucket found for settings"); + Log.wtf(TAG, "No bucket found for settings"); return -1; } } @@ -474,7 +474,7 @@ public class BackgroundScanScheduler { } if (gcd < PERIOD_MIN_GCD_MS) { - Slog.wtf(TAG, "found gcd less than min gcd"); + Log.wtf(TAG, "found gcd less than min gcd"); gcd = PERIOD_MIN_GCD_MS; } @@ -517,7 +517,7 @@ public class BackgroundScanScheduler { } } if (index == -1) { - Slog.wtf(TAG, "Could not find best bucket for period " + requestedPeriod + " in " + Log.wtf(TAG, "Could not find best bucket for period " + requestedPeriod + " in " + maxNumBuckets + " buckets"); } return index; diff --git a/service/java/com/android/server/wifi/scanner/ChannelHelper.java b/service/java/com/android/server/wifi/scanner/ChannelHelper.java index 6a01f0c83d..c2d651daba 100644 --- a/service/java/com/android/server/wifi/scanner/ChannelHelper.java +++ b/service/java/com/android/server/wifi/scanner/ChannelHelper.java @@ -56,6 +56,15 @@ public abstract class ChannelHelper { public abstract WifiScanner.ChannelSpec[] getAvailableScanChannels(int band); /** + * Compares the channels / bands available from this helper with the channels / bands available + * from the other channel helper. + * + * @return true if the all the channels available from the other channel helper is also + * available in this helper. + */ + public abstract boolean satisfies(ChannelHelper otherChannelHelper); + + /** * Estimates the duration that the chip will spend scanning with the given settings */ public abstract int estimateScanDuration(WifiScanner.ScanSettings settings); diff --git a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java index ce92956b9b..12f65958de 100644 --- a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java @@ -39,18 +39,17 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb private static final String TAG = "HalWifiScannerImpl"; private static final boolean DBG = false; - private final String mIfaceName; private final WifiNative mWifiNative; private final ChannelHelper mChannelHelper; private final WificondScannerImpl mWificondScannerDelegate; public HalWifiScannerImpl(Context context, String ifaceName, WifiNative wifiNative, WifiMonitor wifiMonitor, Looper looper, Clock clock) { - mIfaceName = ifaceName; + super(ifaceName); mWifiNative = wifiNative; mChannelHelper = new WificondChannelHelper(wifiNative); mWificondScannerDelegate = - new WificondScannerImpl(context, mIfaceName, wifiNative, wifiMonitor, + new WificondScannerImpl(context, getIfaceName(), wifiNative, wifiMonitor, mChannelHelper, looper, clock); } @@ -68,7 +67,7 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb @Override public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) { return mWifiNative.getBgScanCapabilities( - mIfaceName, capabilities); + getIfaceName(), capabilities); } @Override @@ -95,27 +94,27 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb return false; } return mWifiNative.startBgScan( - mIfaceName, settings, eventHandler); + getIfaceName(), settings, eventHandler); } @Override public void stopBatchedScan() { - mWifiNative.stopBgScan(mIfaceName); + mWifiNative.stopBgScan(getIfaceName()); } @Override public void pauseBatchedScan() { - mWifiNative.pauseBgScan(mIfaceName); + mWifiNative.pauseBgScan(getIfaceName()); } @Override public void restartBatchedScan() { - mWifiNative.restartBgScan(mIfaceName); + mWifiNative.restartBgScan(getIfaceName()); } @Override public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { - return mWifiNative.getBgScanResults(mIfaceName); + return mWifiNative.getBgScanResults(getIfaceName()); } @Override diff --git a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java index 33cce1c080..7aeefce0fe 100644 --- a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java +++ b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java @@ -16,12 +16,24 @@ package com.android.server.wifi.scanner; +import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ; +import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ_WITH_5GHZ_DFS; +import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ; +import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY; +import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS; +import static android.net.wifi.WifiScanner.WIFI_BAND_BOTH; +import static android.net.wifi.WifiScanner.WIFI_BAND_BOTH_WITH_DFS; +import static android.net.wifi.WifiScanner.WIFI_BAND_MAX; +import static android.net.wifi.WifiScanner.WIFI_BAND_UNSPECIFIED; + import android.net.wifi.WifiScanner; import android.util.ArraySet; import com.android.server.wifi.WifiNative; +import java.util.Arrays; import java.util.Set; +import java.util.stream.Collectors; /** * ChannelHelper that offers channel manipulation utilities when the channels in a band are known. @@ -32,36 +44,45 @@ public class KnownBandsChannelHelper extends ChannelHelper { private WifiScanner.ChannelSpec[][] mBandsToChannels; protected void setBandChannels(int[] channels2G, int[] channels5G, int[] channelsDfs) { - mBandsToChannels = new WifiScanner.ChannelSpec[8][]; - - mBandsToChannels[0] = NO_CHANNELS; - - mBandsToChannels[1] = new WifiScanner.ChannelSpec[channels2G.length]; - copyChannels(mBandsToChannels[1], 0, channels2G); - - mBandsToChannels[2] = new WifiScanner.ChannelSpec[channels5G.length]; - copyChannels(mBandsToChannels[2], 0, channels5G); - - mBandsToChannels[3] = new WifiScanner.ChannelSpec[channels2G.length + channels5G.length]; - copyChannels(mBandsToChannels[3], 0, channels2G); - copyChannels(mBandsToChannels[3], channels2G.length, channels5G); - - mBandsToChannels[4] = new WifiScanner.ChannelSpec[channelsDfs.length]; - copyChannels(mBandsToChannels[4], 0, channelsDfs); - - mBandsToChannels[5] = new WifiScanner.ChannelSpec[channels2G.length + channelsDfs.length]; - copyChannels(mBandsToChannels[5], 0, channels2G); - copyChannels(mBandsToChannels[5], channels2G.length, channelsDfs); - - mBandsToChannels[6] = new WifiScanner.ChannelSpec[channels5G.length + channelsDfs.length]; - copyChannels(mBandsToChannels[6], 0, channels5G); - copyChannels(mBandsToChannels[6], channels5G.length, channelsDfs); - - mBandsToChannels[7] = new WifiScanner.ChannelSpec[ - channels2G.length + channels5G.length + channelsDfs.length]; - copyChannels(mBandsToChannels[7], 0, channels2G); - copyChannels(mBandsToChannels[7], channels2G.length, channels5G); - copyChannels(mBandsToChannels[7], channels2G.length + channels5G.length, channelsDfs); + mBandsToChannels = new WifiScanner.ChannelSpec[WIFI_BAND_MAX][]; + + mBandsToChannels[WIFI_BAND_UNSPECIFIED] = NO_CHANNELS; + + mBandsToChannels[WIFI_BAND_24_GHZ] = new WifiScanner.ChannelSpec[channels2G.length]; + copyChannels(mBandsToChannels[WIFI_BAND_24_GHZ], 0, channels2G); + + mBandsToChannels[WIFI_BAND_5_GHZ] = new WifiScanner.ChannelSpec[channels5G.length]; + copyChannels(mBandsToChannels[WIFI_BAND_5_GHZ], 0, channels5G); + + mBandsToChannels[WIFI_BAND_BOTH] = + new WifiScanner.ChannelSpec[channels2G.length + channels5G.length]; + copyChannels(mBandsToChannels[WIFI_BAND_BOTH], 0, channels2G); + copyChannels(mBandsToChannels[WIFI_BAND_BOTH], channels2G.length, channels5G); + + mBandsToChannels[WIFI_BAND_5_GHZ_DFS_ONLY] = + new WifiScanner.ChannelSpec[channelsDfs.length]; + copyChannels(mBandsToChannels[WIFI_BAND_5_GHZ_DFS_ONLY], 0, channelsDfs); + + // No constant for 2G + DFS available. + mBandsToChannels[WIFI_BAND_24_GHZ_WITH_5GHZ_DFS] = + new WifiScanner.ChannelSpec[channels2G.length + channelsDfs.length]; + copyChannels(mBandsToChannels[WIFI_BAND_24_GHZ_WITH_5GHZ_DFS], 0, channels2G); + copyChannels(mBandsToChannels[WIFI_BAND_24_GHZ_WITH_5GHZ_DFS], channels2G.length, + channelsDfs); + + mBandsToChannels[WIFI_BAND_5_GHZ_WITH_DFS] = + new WifiScanner.ChannelSpec[channels5G.length + channelsDfs.length]; + copyChannels(mBandsToChannels[WIFI_BAND_5_GHZ_WITH_DFS], 0, channels5G); + copyChannels(mBandsToChannels[WIFI_BAND_5_GHZ_WITH_DFS], channels5G.length, + channelsDfs); + + mBandsToChannels[WIFI_BAND_BOTH_WITH_DFS] = + new WifiScanner.ChannelSpec[channels2G.length + channels5G.length + + channelsDfs.length]; + copyChannels(mBandsToChannels[WIFI_BAND_BOTH_WITH_DFS], 0, channels2G); + copyChannels(mBandsToChannels[WIFI_BAND_BOTH_WITH_DFS], channels2G.length, channels5G); + copyChannels(mBandsToChannels[WIFI_BAND_BOTH_WITH_DFS], + channels2G.length + channels5G.length, channelsDfs); } private static void copyChannels( @@ -73,7 +94,7 @@ public class KnownBandsChannelHelper extends ChannelHelper { @Override public WifiScanner.ChannelSpec[] getAvailableScanChannels(int band) { - if (band < WifiScanner.WIFI_BAND_24_GHZ || band > WifiScanner.WIFI_BAND_BOTH_WITH_DFS) { + if (band < WIFI_BAND_UNSPECIFIED || band >= WIFI_BAND_MAX) { // invalid value for band return NO_CHANNELS; } else { @@ -82,8 +103,29 @@ public class KnownBandsChannelHelper extends ChannelHelper { } @Override + public boolean satisfies(ChannelHelper otherChannelHelper) { + if (!(otherChannelHelper instanceof KnownBandsChannelHelper)) return false; + KnownBandsChannelHelper otherKnownBandsChannelHelper = + (KnownBandsChannelHelper) otherChannelHelper; + // Compare all the channels in every band + for (int i = WIFI_BAND_UNSPECIFIED; i < WIFI_BAND_MAX; i++) { + Set<Integer> thisFrequencies = Arrays.stream(mBandsToChannels[i]) + .map(spec -> spec.frequency) + .collect(Collectors.toSet()); + Set<Integer> otherFrequencies = Arrays.stream( + otherKnownBandsChannelHelper.mBandsToChannels[i]) + .map(spec -> spec.frequency) + .collect(Collectors.toSet()); + if (!thisFrequencies.containsAll(otherFrequencies)) { + return false; + } + } + return true; + } + + @Override public int estimateScanDuration(WifiScanner.ScanSettings settings) { - if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { + if (settings.band == WIFI_BAND_UNSPECIFIED) { return settings.channels.length * SCAN_PERIOD_PER_CHANNEL_MS; } else { return getAvailableScanChannels(settings.band).length * SCAN_PERIOD_PER_CHANNEL_MS; @@ -103,20 +145,20 @@ public class KnownBandsChannelHelper extends ChannelHelper { // TODO this should be rewritten to be based on the input data instead of hardcoded ranges private int getBandFromChannel(int frequency) { if (2400 <= frequency && frequency < 2500) { - return WifiScanner.WIFI_BAND_24_GHZ; + return WIFI_BAND_24_GHZ; } else if (isDfsChannel(frequency)) { return WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY; } else if (5100 <= frequency && frequency < 6000) { - return WifiScanner.WIFI_BAND_5_GHZ; + return WIFI_BAND_5_GHZ; } else { - return WifiScanner.WIFI_BAND_UNSPECIFIED; + return WIFI_BAND_UNSPECIFIED; } } @Override public boolean settingsContainChannel(WifiScanner.ScanSettings settings, int channel) { WifiScanner.ChannelSpec[] settingsChannels; - if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { + if (settings.band == WIFI_BAND_UNSPECIFIED) { settingsChannels = settings.channels; } else { settingsChannels = getAvailableScanChannels(settings.band); @@ -199,8 +241,7 @@ public class KnownBandsChannelHelper extends ChannelHelper { @Override public boolean isAllChannels() { - return getAvailableScanChannels(WifiScanner.WIFI_BAND_BOTH_WITH_DFS).length == - mChannels.size(); + return getAvailableScanChannels(WIFI_BAND_BOTH_WITH_DFS).length == mChannels.size(); } @Override @@ -251,7 +292,7 @@ public class KnownBandsChannelHelper extends ChannelHelper { bucketSettings.num_channels = 0; bucketSettings.channels = null; } else { - bucketSettings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; + bucketSettings.band = WIFI_BAND_UNSPECIFIED; bucketSettings.num_channels = mChannels.size(); bucketSettings.channels = new WifiNative.ChannelSettings[mChannels.size()]; for (int i = 0; i < mChannels.size(); ++i) { @@ -264,7 +305,7 @@ public class KnownBandsChannelHelper extends ChannelHelper { @Override public Set<Integer> getScanFreqs() { - if (mExactBands == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) { + if (mExactBands == WIFI_BAND_BOTH_WITH_DFS) { return null; } else { return new ArraySet<Integer>(mChannels); diff --git a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java index c08baf536d..c99117fdb9 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java @@ -16,6 +16,7 @@ package com.android.server.wifi.scanner; +import android.annotation.NonNull; import android.content.Context; import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; @@ -40,7 +41,11 @@ public abstract class WifiScannerImpl { * A factory that create a {@link com.android.server.wifi.scanner.WifiScannerImpl} */ public static interface WifiScannerImplFactory { - WifiScannerImpl create(Context context, Looper looper, Clock clock); + /** + * Create instance of {@link WifiScannerImpl}. + */ + WifiScannerImpl create(Context context, Looper looper, Clock clock, + @NonNull String ifaceName); } /** @@ -48,10 +53,10 @@ public abstract class WifiScannerImpl { * This factory should only ever be used once. */ public static final WifiScannerImplFactory DEFAULT_FACTORY = new WifiScannerImplFactory() { - public WifiScannerImpl create(Context context, Looper looper, Clock clock) { + public WifiScannerImpl create(Context context, Looper looper, Clock clock, + @NonNull String ifaceName) { WifiNative wifiNative = WifiInjector.getInstance().getWifiNative(); WifiMonitor wifiMonitor = WifiInjector.getInstance().getWifiMonitor(); - String ifaceName = wifiNative.getClientInterfaceName(); if (TextUtils.isEmpty(ifaceName)) { return null; } @@ -76,6 +81,19 @@ public abstract class WifiScannerImpl { } }; + private final String mIfaceName; + + WifiScannerImpl(@NonNull String ifaceName) { + mIfaceName = ifaceName; + } + + /** + * Get the interface name used by this instance of {@link WifiScannerImpl} + */ + public @NonNull String getIfaceName() { + return mIfaceName; + } + /** * Cleanup any ongoing operations. This may be called when the driver is unloaded. * There is no expectation that failure events are returned for ongoing operations. diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningService.java b/service/java/com/android/server/wifi/scanner/WifiScanningService.java index 93ac722529..d8dec0fb82 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningService.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningService.java @@ -17,40 +17,40 @@ package com.android.server.wifi.scanner; import android.content.Context; +import android.os.Binder; import android.os.HandlerThread; import android.util.Log; -import com.android.server.SystemService; import com.android.server.am.BatteryStatsService; import com.android.server.wifi.WifiInjector; +import com.android.server.wifi.WifiServiceBase; -public class WifiScanningService extends SystemService { +/** + * Manages the wifi scanner service instance. + */ +public class WifiScanningService implements WifiServiceBase { static final String TAG = "WifiScanningService"; private final WifiScanningServiceImpl mImpl; private final HandlerThread mHandlerThread; public WifiScanningService(Context context) { - super(context); Log.i(TAG, "Creating " + Context.WIFI_SCANNING_SERVICE); mHandlerThread = new HandlerThread("WifiScanningService"); mHandlerThread.start(); - mImpl = new WifiScanningServiceImpl(getContext(), mHandlerThread.getLooper(), + mImpl = new WifiScanningServiceImpl(context, mHandlerThread.getLooper(), WifiScannerImpl.DEFAULT_FACTORY, BatteryStatsService.getService(), WifiInjector.getInstance()); } @Override public void onStart() { - Log.i(TAG, "Publishing " + Context.WIFI_SCANNING_SERVICE); - publishBinderService(Context.WIFI_SCANNING_SERVICE, mImpl); + Log.i(TAG, "Starting " + Context.WIFI_SCANNING_SERVICE); + mImpl.startService(); } @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - Log.i(TAG, "Starting " + Context.WIFI_SCANNING_SERVICE); - mImpl.startService(); - } + public Binder retrieveImpl() { + return mImpl; } } diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index 442faa13e1..4d21ac6551 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -16,10 +16,10 @@ package com.android.server.wifi.scanner; -import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AlarmManager; import android.content.Context; import android.net.wifi.IWifiScanner; @@ -29,6 +29,8 @@ 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.net.wifi.WifiScanner.WifiBand; +import android.net.wifi.WifiStackClient; import android.os.Binder; import android.os.Bundle; import android.os.Looper; @@ -37,6 +39,7 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.WorkSource; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.LocalLog; import android.util.Log; import android.util.Pair; @@ -69,6 +72,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Set; public class WifiScanningServiceImpl extends IWifiScanner.Stub { @@ -95,8 +100,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mLocalLog.log(message); } - private WifiScannerImpl mScannerImpl; - @Override public Messenger getMessenger() { if (mClientHandler != null) { @@ -108,10 +111,12 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } @Override - public Bundle getAvailableChannels(int band) { + public Bundle getAvailableChannels(@WifiBand int band, String packageName) { + enforcePermission(Binder.getCallingUid(), packageName, false, false, false); + mChannelHelper.updateChannels(); ChannelSpec[] channelSpecs = mChannelHelper.getAvailableScanChannels(band); - ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length); + ArrayList<Integer> list = new ArrayList<>(channelSpecs.length); for (ChannelSpec channelSpec : channelSpecs) { list.add(channelSpec.frequency); } @@ -121,11 +126,16 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { return b; } - private void enforceNetworkStack(int uid) { + private void enforceWifiStackPermission(int uid) { mContext.enforcePermission( - Manifest.permission.NETWORK_STACK, + WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK, UNKNOWN_PID, uid, - "NetworkStack"); + "MainlineWifiStack"); + } + + private boolean checkWifiStackPermission(int uid) { + return mContext.checkPermission(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK, + UNKNOWN_PID, uid) == PERMISSION_GRANTED; } // Helper method to check if the incoming message is for a privileged request. @@ -166,29 +176,39 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } /** + * @see #enforcePermission(int, String, boolean, boolean, boolean) + */ + private void enforcePermission(int uid, Message msg) throws SecurityException { + enforcePermission(uid, getPackageName(msg), isPrivilegedMessage(msg.what), + shouldIgnoreLocationSettingsForSingleScan(msg), + shouldHideFromAppsForSingleScan(msg)); + } + + /** * Enforce the necessary client permissions for WifiScanner. * If the client has NETWORK_STACK permission, then it can "always" send "any" request. * If the client has only LOCATION_HARDWARE permission, then it can * a) Only make scan related requests when location is turned on. * b) Can never make one of the privileged requests. - * - * @param uid Uid of the client. - * @param msg {@link Message} of the incoming request. - * @throws {@link SecurityException} if the client does not have the necessary permissions. + * @param uid uid of the client + * @param packageName package name of the client + * @param isPrivilegedRequest whether we are checking for a privileged request + * @param shouldIgnoreLocationSettings override to ignore location settings + * @param shouldHideFromApps override to hide request from AppOps */ - private void enforcePermission(int uid, Message msg) throws SecurityException { + private void enforcePermission(int uid, String packageName, boolean isPrivilegedRequest, + boolean shouldIgnoreLocationSettings, boolean shouldHideFromApps) { try { - /** Wifi stack issued requests.*/ - enforceNetworkStack(uid); + // Wifi stack issued requests. + enforceWifiStackPermission(uid); } catch (SecurityException e) { - /** System-app issued requests. */ - if (isPrivilegedMessage(msg.what)) { + // System-app issued requests + if (isPrivilegedRequest) { // Privileged message, only requests from clients with NETWORK_STACK allowed! throw e; } - mWifiPermissionsUtil.enforceCanAccessScanResultsForWifiScanner( - getPackageName(msg), uid, shouldIgnoreLocationSettingsForSingleScan(msg), - shouldHideFromAppsForSingleScan(msg)); + mWifiPermissionsUtil.enforceCanAccessScanResultsForWifiScanner(packageName, uid, + shouldIgnoreLocationSettings, shouldHideFromApps); } } @@ -276,14 +296,16 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { switch (msg.what) { case WifiScanner.CMD_ENABLE: - mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_LOADED); - mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED); - mPnoScanStateMachine.sendMessage(CMD_DRIVER_LOADED); + setupScannerImpls(); + mBackgroundScanStateMachine.sendMessage(Message.obtain(msg)); + mSingleScanStateMachine.sendMessage(Message.obtain(msg)); + mPnoScanStateMachine.sendMessage(Message.obtain(msg)); break; case WifiScanner.CMD_DISABLE: - mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); - mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); - mPnoScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); + teardownScannerImpls(); + mBackgroundScanStateMachine.sendMessage(Message.obtain(msg)); + mSingleScanStateMachine.sendMessage(Message.obtain(msg)); + mPnoScanStateMachine.sendMessage(Message.obtain(msg)); break; case WifiScanner.CMD_START_BACKGROUND_SCAN: case WifiScanner.CMD_STOP_BACKGROUND_SCAN: @@ -317,8 +339,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private static final int CMD_SCAN_RESULTS_AVAILABLE = BASE + 0; private static final int CMD_FULL_SCAN_RESULTS = BASE + 1; - private static final int CMD_DRIVER_LOADED = BASE + 6; - private static final int CMD_DRIVER_UNLOADED = BASE + 7; private static final int CMD_SCAN_PAUSED = BASE + 8; private static final int CMD_SCAN_RESTARTED = BASE + 9; private static final int CMD_SCAN_FAILED = BASE + 10; @@ -329,6 +349,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final Looper mLooper; private final WifiScannerImpl.WifiScannerImplFactory mScannerImplFactory; private final ArrayMap<Messenger, ClientInfo> mClients; + private final Map<String, WifiScannerImpl> mScannerImpls; + private final RequestList<Void> mSingleScanListeners = new RequestList<>(); @@ -347,6 +369,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final Clock mClock; private final FrameworkFacade mFrameworkFacade; private final WifiPermissionsUtil mWifiPermissionsUtil; + private final WifiNative mWifiNative; WifiScanningServiceImpl(Context context, Looper looper, WifiScannerImpl.WifiScannerImplFactory scannerImplFactory, IBatteryStats batteryStats, @@ -356,12 +379,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mScannerImplFactory = scannerImplFactory; mBatteryStats = batteryStats; mClients = new ArrayMap<>(); + mScannerImpls = new ArrayMap<>(); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mWifiMetrics = wifiInjector.getWifiMetrics(); mClock = wifiInjector.getClock(); mLog = wifiInjector.makeLog(TAG); mFrameworkFacade = wifiInjector.getFrameworkFacade(); mWifiPermissionsUtil = wifiInjector.getWifiPermissionsUtil(); + mWifiNative = wifiInjector.getWifiNative(); mPreviousSchedule = null; } @@ -379,6 +404,81 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } /** + * Checks if all the channels provided by the new impl is already satisfied by an existing impl. + * + * Note: This only handles the cases where the 2 ifaces are on different chips with + * distinctly different bands supported on both. If there are cases where + * the 2 ifaces support overlapping bands, then we probably need to rework this. + * For example: wlan0 supports 2.4G only, wlan1 supports 2.4G + 5G + DFS. + * In the above example, we should teardown wlan0 impl when wlan1 impl is created + * because wlan1 impl can already handle all the supported bands. + * Ignoring this for now since we don't foresee this requirement in the near future. + */ + private boolean doesAnyExistingImplSatisfy(WifiScannerImpl newImpl) { + for (WifiScannerImpl existingImpl : mScannerImpls.values()) { + if (existingImpl.getChannelHelper().satisfies(newImpl.getChannelHelper())) { + return true; + } + } + return false; + } + + private void setupScannerImpls() { + Set<String> ifaceNames = mWifiNative.getClientInterfaceNames(); + if (ArrayUtils.isEmpty(ifaceNames)) { + loge("Failed to retrieve client interface names"); + return; + } + Set<String> ifaceNamesOfImplsAlreadySetup = mScannerImpls.keySet(); + if (ifaceNames.equals(ifaceNamesOfImplsAlreadySetup)) { + // Scanner Impls already exist for all ifaces (back to back CMD_ENABLE sent?). + Log.i(TAG, "scanner impls already exists"); + return; + } + // set of impls to teardown. + Set<String> ifaceNamesOfImplsToTeardown = new ArraySet<>(ifaceNamesOfImplsAlreadySetup); + ifaceNamesOfImplsToTeardown.removeAll(ifaceNames); + // set of impls to be considered for setup. + Set<String> ifaceNamesOfImplsToSetup = new ArraySet<>(ifaceNames); + ifaceNamesOfImplsToSetup.removeAll(ifaceNamesOfImplsAlreadySetup); + + for (String ifaceName : ifaceNamesOfImplsToTeardown) { + WifiScannerImpl impl = mScannerImpls.remove(ifaceName); + if (impl == null) continue; // should never happen + impl.cleanup(); + Log.i(TAG, "Removed an impl for " + ifaceName); + } + for (String ifaceName : ifaceNamesOfImplsToSetup) { + WifiScannerImpl impl = mScannerImplFactory.create(mContext, mLooper, mClock, ifaceName); + if (impl == null) { + loge("Failed to create scanner impl for " + ifaceName); + continue; + } + // If this new scanner impl does not offer any new bands to scan, then we should + // ignore it. + if (!doesAnyExistingImplSatisfy(impl)) { + mScannerImpls.put(ifaceName, impl); + Log.i(TAG, "Created a new impl for " + ifaceName); + } else { + Log.i(TAG, "All the channels on the new impl for iface " + ifaceName + + " are already satisfied by an existing impl. Skipping.."); + impl.cleanup(); // cleanup the impl before discarding. + } + } + } + + private void teardownScannerImpls() { + for (Map.Entry<String, WifiScannerImpl> entry : mScannerImpls.entrySet()) { + WifiScannerImpl impl = entry.getValue(); + String ifaceName = entry.getKey(); + if (impl == null) continue; // should never happen + impl.cleanup(); + Log.i(TAG, "Removed an impl for " + ifaceName); + } + mScannerImpls.clear(); + } + + /** * Provide a way for unit tests to set valid log object in the WifiHandler * @param log WifiLog object to assign to the clientHandler */ @@ -491,7 +591,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { * ScanningState. Any requests queued while scanning will be placed in the pending queue and * executed after transitioning back to IdleState. */ - class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler { + class WifiSingleScanStateMachine extends StateMachine { /** * Maximum age of results that we return from our cache via * {@link WifiScanner#getScanResults()}. @@ -513,9 +613,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { // Scan results cached from the last full single scan request. private final List<ScanResult> mCachedScanResults = new ArrayList<>(); + // Tracks scan requests across multiple scanner impls. + private final ScannerImplsTracker mScannerImplsTracker; + WifiSingleScanStateMachine(Looper looper) { super("WifiSingleScanStateMachine", looper); + mScannerImplsTracker = new ScannerImplsTracker(); + setLogRecSize(128); setLogOnlyTransitions(false); @@ -530,46 +635,150 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } /** - * Called to indicate a change in state for the current scan. - * Will dispatch a coresponding event to the state machine + * Tracks a single scan request across all the available scanner impls. + * + * a) Initiates the scan using the same ScanSettings across all the available impls. + * b) Waits for all the impls to report the status of the scan request (success or failure). + * c) Calculates a consolidated scan status and sends the results if successful. + * Note: If there are failures on some of the scanner impls, we ignore them since we will + * get some scan results from the other successful impls. We don't declare total scan + * failures, unless all the scanner impls fail. */ - @Override - public void onScanStatus(int event) { - if (DBG) localLog("onScanStatus event received, event=" + event); - switch(event) { - case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: - case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: - case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: - sendMessage(CMD_SCAN_RESULTS_AVAILABLE); - break; - case WifiNative.WIFI_SCAN_FAILED: - sendMessage(CMD_SCAN_FAILED); - break; - default: - Log.e(TAG, "Unknown scan status event: " + event); - break; + private final class ScannerImplsTracker { + private final class ScanEventHandler implements WifiNative.ScanEventHandler { + private final String mImplIfaceName; + ScanEventHandler(@NonNull String implIfaceName) { + mImplIfaceName = implIfaceName; + } + + /** + * Called to indicate a change in state for the current scan. + * Will dispatch a corresponding event to the state machine + */ + @Override + public void onScanStatus(int event) { + if (DBG) localLog("onScanStatus event received, event=" + event); + switch (event) { + case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: + case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: + case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: + reportScanStatusForImpl(mImplIfaceName, STATUS_SUCCEEDED); + break; + case WifiNative.WIFI_SCAN_FAILED: + reportScanStatusForImpl(mImplIfaceName, STATUS_FAILED); + break; + default: + Log.e(TAG, "Unknown scan status event: " + event); + break; + } + } + + /** + * Called for each full scan result if requested + */ + @Override + public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) { + if (DBG) localLog("onFullScanResult received"); + reportFullScanResultForImpl(mImplIfaceName, fullScanResult, bucketsScanned); + } + + @Override + public void onScanPaused(ScanData[] scanData) { + // should not happen for single scan + Log.e(TAG, "Got scan paused for single scan"); + } + + @Override + public void onScanRestarted() { + // should not happen for single scan + Log.e(TAG, "Got scan restarted for single scan"); + } } - } - /** - * Called for each full scan result if requested - */ - @Override - public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) { - if (DBG) localLog("onFullScanResult received"); - sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult); - } + private static final int STATUS_PENDING = 0; + private static final int STATUS_SUCCEEDED = 1; + private static final int STATUS_FAILED = 2; - @Override - public void onScanPaused(ScanData[] scanData) { - // should not happen for single scan - Log.e(TAG, "Got scan paused for single scan"); - } + // Tracks scan status per impl. + Map<String, Integer> mStatusPerImpl = new ArrayMap<>(); - @Override - public void onScanRestarted() { - // should not happen for single scan - Log.e(TAG, "Got scan restarted for single scan"); + /** + * Triggers a new scan on all the available scanner impls. + * @return true if the scan succeeded on any of the impl, false otherwise. + */ + public boolean startSingleScan(WifiNative.ScanSettings scanSettings) { + mStatusPerImpl.clear(); + boolean anySuccess = false; + for (Map.Entry<String, WifiScannerImpl> entry : mScannerImpls.entrySet()) { + String ifaceName = entry.getKey(); + WifiScannerImpl impl = entry.getValue(); + boolean success = impl.startSingleScan( + scanSettings, new ScanEventHandler(ifaceName)); + if (!success) { + Log.e(TAG, "Failed to start single scan on " + ifaceName); + continue; + } + mStatusPerImpl.put(ifaceName, STATUS_PENDING); + anySuccess = true; + } + return anySuccess; + } + + /** + * Returns the latest scan results from all the available scanner impls. + * @return Consolidated list of scan results from all the impl. + */ + public @Nullable ScanData getLatestSingleScanResults() { + ScanData consolidatedScanData = null; + for (WifiScannerImpl impl : mScannerImpls.values()) { + ScanData scanData = impl.getLatestSingleScanResults(); + if (consolidatedScanData == null) { + consolidatedScanData = new ScanData(scanData); + } else { + consolidatedScanData.addResults(scanData.getResults()); + } + } + return consolidatedScanData; + } + + private void reportFullScanResultForImpl(@NonNull String implIfaceName, + ScanResult fullScanResult, int bucketsScanned) { + Integer status = mStatusPerImpl.get(implIfaceName); + if (status != null && status == STATUS_PENDING) { + sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult); + } + } + + private int getConsolidatedStatus() { + boolean anyPending = mStatusPerImpl.values().stream() + .anyMatch(status -> status == STATUS_PENDING); + // at-least one impl status is still pending. + if (anyPending) return STATUS_PENDING; + + boolean anySuccess = mStatusPerImpl.values().stream() + .anyMatch(status -> status == STATUS_SUCCEEDED); + // one success is good enough to declare consolidated success. + if (anySuccess) { + return STATUS_SUCCEEDED; + } else { + // all failed. + return STATUS_FAILED; + } + } + + private void reportScanStatusForImpl(@NonNull String implIfaceName, int newStatus) { + Integer currentStatus = mStatusPerImpl.get(implIfaceName); + if (currentStatus != null && currentStatus == STATUS_PENDING) { + mStatusPerImpl.put(implIfaceName, newStatus); + } + // Now check if all the scanner impls scan status is available. + int consolidatedStatus = getConsolidatedStatus(); + if (consolidatedStatus == STATUS_SUCCEEDED) { + sendMessage(CMD_SCAN_RESULTS_AVAILABLE); + } else if (consolidatedStatus == STATUS_FAILED) { + sendMessage(CMD_SCAN_FAILED); + } + } } class DefaultState extends State { @@ -581,15 +790,15 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public boolean processMessage(Message msg) { switch (msg.what) { - case CMD_DRIVER_LOADED: - if (mScannerImpl == null) { + case WifiScanner.CMD_ENABLE: + if (mScannerImpls.isEmpty()) { loge("Failed to start single scan state machine because scanner impl" + " is null"); return HANDLED; } transitionTo(mIdleState); return HANDLED; - case CMD_DRIVER_UNLOADED: + case WifiScanner.CMD_DISABLE: transitionTo(mDefaultState); return HANDLED; case WifiScanner.CMD_START_SINGLE_SCAN: @@ -653,7 +862,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo ci = mClients.get(msg.replyTo); switch (msg.what) { - case CMD_DRIVER_LOADED: + case WifiScanner.CMD_ENABLE: // Ignore if we're already in driver loaded state. return HANDLED; case WifiScanner.CMD_START_SINGLE_SCAN: @@ -761,11 +970,17 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { public boolean processMessage(Message msg) { switch (msg.what) { case CMD_SCAN_RESULTS_AVAILABLE: - mWifiMetrics.incrementScanReturnEntry( - WifiMetricsProto.WifiLog.SCAN_SUCCESS, - mActiveScans.size()); - reportScanResults(mScannerImpl.getLatestSingleScanResults()); - mActiveScans.clear(); + ScanData latestScanResults = + mScannerImplsTracker.getLatestSingleScanResults(); + if (latestScanResults != null) { + mWifiMetrics.incrementScanReturnEntry( + WifiMetricsProto.WifiLog.SCAN_SUCCESS, + mActiveScans.size()); + reportScanResults(latestScanResults); + mActiveScans.clear(); + } else { + Log.e(TAG, "latest scan results null unexpectedly"); + } transitionTo(mIdleState); return HANDLED; case CMD_FULL_SCAN_RESULTS: @@ -804,9 +1019,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { Log.e(TAG, "Invalid scan type " + settings.type); return false; } - if (mContext.checkPermission( - Manifest.permission.NETWORK_STACK, UNKNOWN_PID, ci.getUid()) - == PERMISSION_DENIED) { + if (!checkWifiStackPermission(ci.getUid())) { if (!ArrayUtils.isEmpty(settings.hiddenNetworks)) { Log.e(TAG, "Failing single scan because app " + ci.getUid() + " does not have permission to set hidden networks"); @@ -974,7 +1187,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE); settings.buckets = new WifiNative.BucketSettings[] {bucketSettings}; - if (mScannerImpl.startSingleScan(settings, this)) { + if (mScannerImplsTracker.startSingleScan(settings)) { // store the active scan settings mActiveScanSettings = settings; // swap pending and active scan requests @@ -1004,7 +1217,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { clientHandlers.clear(); } - void reportFullScanResult(ScanResult result, int bucketsScanned) { + void reportFullScanResult(@NonNull ScanResult result, int bucketsScanned) { for (RequestInfo<ScanSettings> entry : mActiveScans) { if (ScanScheduleUtil.shouldReportFullScanResultForSettings(mChannelHelper, result, bucketsScanned, entry.settings, -1)) { @@ -1017,7 +1230,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } - void reportScanResults(ScanData results) { + void reportScanResults(@NonNull ScanData results) { if (results != null && results.getResults() != null) { if (results.getResults().length > 0) { mWifiMetrics.incrementNonEmptyScanResultCount(); @@ -1059,8 +1272,9 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } - class WifiBackgroundScanStateMachine extends StateMachine - implements WifiNative.ScanEventHandler { + // TODO(b/71855918): Remove this bg scan state machine and its dependencies. + // Note: bgscan will not support multiple scanner impls (will pick any). + class WifiBackgroundScanStateMachine extends StateMachine { private final DefaultState mDefaultState = new DefaultState(); private final StartedState mStartedState = new StartedState(); @@ -1068,6 +1282,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final RequestList<ScanSettings> mActiveBackgroundScans = new RequestList<>(); + private WifiScannerImpl mScannerImpl; + WifiBackgroundScanStateMachine(Looper looper) { super("WifiBackgroundScanStateMachine", looper); @@ -1092,40 +1308,48 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { updateSchedule(); } - @Override - public void onScanStatus(int event) { - if (DBG) localLog("onScanStatus event received, event=" + event); - switch(event) { - case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: - case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: - case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: - sendMessage(CMD_SCAN_RESULTS_AVAILABLE); - break; - case WifiNative.WIFI_SCAN_FAILED: - sendMessage(CMD_SCAN_FAILED); - break; - default: - Log.e(TAG, "Unknown scan status event: " + event); - break; + private final class ScanEventHandler implements WifiNative.ScanEventHandler { + private final String mImplIfaceName; + + ScanEventHandler(@NonNull String implIfaceName) { + mImplIfaceName = implIfaceName; } - } - @Override - public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) { - if (DBG) localLog("onFullScanResult received"); - sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult); - } + @Override + public void onScanStatus(int event) { + if (DBG) localLog("onScanStatus event received, event=" + event); + switch (event) { + case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: + case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: + case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: + sendMessage(CMD_SCAN_RESULTS_AVAILABLE); + break; + case WifiNative.WIFI_SCAN_FAILED: + sendMessage(CMD_SCAN_FAILED); + break; + default: + Log.e(TAG, "Unknown scan status event: " + event); + break; + } + } - @Override - public void onScanPaused(ScanData scanData[]) { - if (DBG) localLog("onScanPaused received"); - sendMessage(CMD_SCAN_PAUSED, scanData); - } + @Override + public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) { + if (DBG) localLog("onFullScanResult received"); + sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult); + } - @Override - public void onScanRestarted() { - if (DBG) localLog("onScanRestarted received"); - sendMessage(CMD_SCAN_RESTARTED); + @Override + public void onScanPaused(ScanData[] scanData) { + if (DBG) localLog("onScanPaused received"); + sendMessage(CMD_SCAN_PAUSED, scanData); + } + + @Override + public void onScanRestarted() { + if (DBG) localLog("onScanRestarted received"); + sendMessage(CMD_SCAN_RESTARTED); + } } class DefaultState extends State { @@ -1138,16 +1362,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public boolean processMessage(Message msg) { switch (msg.what) { - case CMD_DRIVER_LOADED: - // TODO this should be moved to a common location since it is used outside - // of this state machine. It is ok right now because the driver loaded event - // is sent to this state machine first. - mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock); - if (mScannerImpl == null) { + case WifiScanner.CMD_ENABLE: + if (mScannerImpls.isEmpty()) { loge("Failed to start bgscan scan state machine because scanner impl" + " is null"); return HANDLED; } + // Pick any impl available and stick to it until disable. + mScannerImpl = mScannerImpls.entrySet().iterator().next().getValue(); mChannelHelper = mScannerImpl.getChannelHelper(); mBackgroundScheduler = new BackgroundScanScheduler(mChannelHelper); @@ -1171,7 +1393,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { transitionTo(mStartedState); return HANDLED; - case CMD_DRIVER_UNLOADED: + case WifiScanner.CMD_DISABLE: Log.i(TAG, "wifi driver unloaded"); transitionTo(mDefaultState); break; @@ -1204,15 +1426,18 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public void enter() { if (DBG) localLog("StartedState"); + if (mScannerImpl == null) { + // should never happen + Log.wtf(TAG, "Scanner impl unexpectedly null"); + transitionTo(mDefaultState); + } } @Override public void exit() { sendBackgroundScanFailedToAllAndClear( WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); - if (mScannerImpl != null) { - mScannerImpl.cleanup(); - } + mScannerImpl = null; // reset impl } @Override @@ -1220,11 +1445,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo ci = mClients.get(msg.replyTo); switch (msg.what) { - case CMD_DRIVER_LOADED: + case WifiScanner.CMD_ENABLE: Log.e(TAG, "wifi driver loaded received while already loaded"); // Ignore if we're already in driver loaded state. return HANDLED; - case CMD_DRIVER_UNLOADED: + case WifiScanner.CMD_DISABLE: return NOT_HANDLED; case WifiScanner.CMD_START_BACKGROUND_SCAN: { mWifiMetrics.incrementBackgroundScanCount(); @@ -1392,7 +1617,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { + ChannelHelper.toString(bucket)); } - if (mScannerImpl.startBatchedScan(schedule, this)) { + if (mScannerImpl.startBatchedScan(schedule, + new ScanEventHandler(mScannerImpl.getIfaceName()))) { if (DBG) { Log.d(TAG, "scan restarted with " + schedule.num_buckets + " bucket(s) and base period: " + schedule.base_period_ms); @@ -1505,7 +1731,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { * multiple requests at the same time, so will need non-trivial changes to support (if at all * possible) in WifiScanningService. */ - class WifiPnoScanStateMachine extends StateMachine implements WifiNative.PnoEventHandler { + class WifiPnoScanStateMachine extends StateMachine { private final DefaultState mDefaultState = new DefaultState(); private final StartedState mStartedState = new StartedState(); @@ -1515,10 +1741,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final RequestList<Pair<PnoSettings, ScanSettings>> mActivePnoScans = new RequestList<>(); + // Tracks scan requests across multiple scanner impls. + private final ScannerImplsTracker mScannerImplsTracker; WifiPnoScanStateMachine(Looper looper) { super("WifiPnoScanStateMachine", looper); + mScannerImplsTracker = new ScannerImplsTracker(); + setLogRecSize(256); setLogOnlyTransitions(false); @@ -1537,16 +1767,125 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { transitionTo(mStartedState); } - @Override - public void onPnoNetworkFound(ScanResult[] results) { - if (DBG) localLog("onWifiPnoNetworkFound event received"); - sendMessage(CMD_PNO_NETWORK_FOUND, 0, 0, results); - } + /** + * Tracks a PNO scan request across all the available scanner impls. + * + * Note: If there are failures on some of the scanner impls, we ignore them since we can + * get a PNO match from the other successful impls. We don't declare total scan + * failures, unless all the scanner impls fail. + */ + private final class ScannerImplsTracker { + private final class PnoEventHandler implements WifiNative.PnoEventHandler { + private final String mImplIfaceName; - @Override - public void onPnoScanFailed() { - if (DBG) localLog("onWifiPnoScanFailed event received"); - sendMessage(CMD_PNO_SCAN_FAILED, 0, 0, null); + PnoEventHandler(@NonNull String implIfaceName) { + mImplIfaceName = implIfaceName; + } + + @Override + public void onPnoNetworkFound(ScanResult[] results) { + if (DBG) localLog("onWifiPnoNetworkFound event received"); + reportPnoNetworkFoundForImpl(mImplIfaceName, results); + } + + @Override + public void onPnoScanFailed() { + if (DBG) localLog("onWifiPnoScanFailed event received"); + reportPnoScanFailedForImpl(mImplIfaceName); + } + } + + private static final int STATUS_PENDING = 0; + private static final int STATUS_FAILED = 2; + + // Tracks scan status per impl. + Map<String, Integer> mStatusPerImpl = new ArrayMap<>(); + + /** + * Triggers a new PNO with the specified settings on all the available scanner impls. + * @return true if the PNO succeeded on any of the impl, false otherwise. + */ + public boolean setHwPnoList(WifiNative.PnoSettings pnoSettings) { + mStatusPerImpl.clear(); + boolean anySuccess = false; + for (Map.Entry<String, WifiScannerImpl> entry : mScannerImpls.entrySet()) { + String ifaceName = entry.getKey(); + WifiScannerImpl impl = entry.getValue(); + boolean success = impl.setHwPnoList( + pnoSettings, new PnoEventHandler(ifaceName)); + if (!success) { + Log.e(TAG, "Failed to start pno on " + ifaceName); + continue; + } + mStatusPerImpl.put(ifaceName, STATUS_PENDING); + anySuccess = true; + } + return anySuccess; + } + + /** + * Resets any ongoing PNO on all the available scanner impls. + * @return true if the PNO stop succeeded on all of the impl, false otherwise. + */ + public boolean resetHwPnoList() { + boolean allSuccess = true; + for (String ifaceName : mStatusPerImpl.keySet()) { + WifiScannerImpl impl = mScannerImpls.get(ifaceName); + if (impl == null) continue; + boolean success = impl.resetHwPnoList(); + if (!success) { + Log.e(TAG, "Failed to stop pno on " + ifaceName); + allSuccess = false; + } + } + mStatusPerImpl.clear(); + return allSuccess; + } + + /** + * @return true if HW PNO is supported on all the available scanner impls, + * false otherwise. + */ + public boolean isHwPnoSupported(boolean isConnected) { + for (WifiScannerImpl impl : mScannerImpls.values()) { + if (!impl.isHwPnoSupported(isConnected)) { + return false; + } + } + return true; + } + + private void reportPnoNetworkFoundForImpl(@NonNull String implIfaceName, + ScanResult[] results) { + Integer status = mStatusPerImpl.get(implIfaceName); + if (status != null && status == STATUS_PENDING) { + sendMessage(CMD_PNO_NETWORK_FOUND, 0, 0, results); + } + } + + private int getConsolidatedStatus() { + boolean anyPending = mStatusPerImpl.values().stream() + .anyMatch(status -> status == STATUS_PENDING); + // at-least one impl status is still pending. + if (anyPending) { + return STATUS_PENDING; + } else { + // all failed. + return STATUS_FAILED; + } + } + + private void reportPnoScanFailedForImpl(@NonNull String implIfaceName) { + Integer currentStatus = mStatusPerImpl.get(implIfaceName); + if (currentStatus != null && currentStatus == STATUS_PENDING) { + mStatusPerImpl.put(implIfaceName, STATUS_FAILED); + } + // Now check if all the scanner impls scan status is available. + int consolidatedStatus = getConsolidatedStatus(); + if (consolidatedStatus == STATUS_FAILED) { + sendMessage(CMD_PNO_SCAN_FAILED); + } + } } class DefaultState extends State { @@ -1558,15 +1897,15 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public boolean processMessage(Message msg) { switch (msg.what) { - case CMD_DRIVER_LOADED: - if (mScannerImpl == null) { + case WifiScanner.CMD_ENABLE: + if (mScannerImpls.isEmpty()) { loge("Failed to start pno scan state machine because scanner impl" + " is null"); return HANDLED; } transitionTo(mStartedState); break; - case CMD_DRIVER_UNLOADED: + case WifiScanner.CMD_DISABLE: transitionTo(mDefaultState); break; case WifiScanner.CMD_START_PNO_SCAN: @@ -1602,7 +1941,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { public boolean processMessage(Message msg) { ClientInfo ci = mClients.get(msg.replyTo); switch (msg.what) { - case CMD_DRIVER_LOADED: + case WifiScanner.CMD_ENABLE: // Ignore if we're already in driver loaded state. return HANDLED; case WifiScanner.CMD_START_PNO_SCAN: @@ -1614,7 +1953,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { pnoParams.setDefusable(true); PnoSettings pnoSettings = pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY); - if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) { + if (mScannerImplsTracker.isHwPnoSupported(pnoSettings.isConnected)) { deferMessage(msg); transitionTo(mHwPnoScanState); } else { @@ -1640,7 +1979,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public void exit() { // Reset PNO scan in ScannerImpl before we exit. - mScannerImpl.resetHwPnoList(); + mScannerImplsTracker.resetHwPnoList(); removeInternalClient(); } @@ -1806,7 +2145,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } WifiNative.PnoSettings nativePnoSettings = convertSettingsToPnoNative(scanSettings, pnoSettings); - if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) { + if (!mScannerImplsTracker.setHwPnoList(nativePnoSettings)) { return false; } logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings); @@ -2182,8 +2521,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ScanResultUtil.dumpScanResults(pw, scanResults, nowMs); pw.println(); } - if (mScannerImpl != null) { - mScannerImpl.dump(fd, pw, args); + for (WifiScannerImpl impl : mScannerImpls.values()) { + impl.dump(fd, pw, args); } } diff --git a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java index e3bd207dfb..c8319c6ec5 100644 --- a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java @@ -31,6 +31,7 @@ import com.android.server.wifi.ScanDetail; import com.android.server.wifi.WifiMonitor; import com.android.server.wifi.WifiNative; import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; +import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.ScanResultUtil; import java.io.FileDescriptor; @@ -60,7 +61,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call private static final int MAX_SCAN_BUCKETS = 16; private final Context mContext; - private final String mIfaceName; private final WifiNative mWifiNative; private final WifiMonitor mWifiMonitor; private final AlarmManager mAlarmManager; @@ -96,8 +96,8 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call public WificondScannerImpl(Context context, String ifaceName, WifiNative wifiNative, WifiMonitor wifiMonitor, ChannelHelper channelHelper, Looper looper, Clock clock) { + super(ifaceName); mContext = context; - mIfaceName = ifaceName; mWifiNative = wifiNative; mWifiMonitor = wifiMonitor; mChannelHelper = channelHelper; @@ -109,11 +109,11 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call mHwPnoScanSupported = mContext.getResources().getBoolean( R.bool.config_wifi_background_scan_support); - wifiMonitor.registerHandler(mIfaceName, + wifiMonitor.registerHandler(getIfaceName(), WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); - wifiMonitor.registerHandler(mIfaceName, + wifiMonitor.registerHandler(getIfaceName(), WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler); - wifiMonitor.registerHandler(mIfaceName, + wifiMonitor.registerHandler(getIfaceName(), WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); } @@ -123,11 +123,11 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call stopHwPnoScan(); mLastScanSettings = null; // finally clear any active scan mLastPnoScanSettings = null; // finally clear any active scan - mWifiMonitor.deregisterHandler(mIfaceName, + mWifiMonitor.deregisterHandler(getIfaceName(), WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); - mWifiMonitor.deregisterHandler(mIfaceName, + mWifiMonitor.deregisterHandler(getIfaceName(), WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler); - mWifiMonitor.deregisterHandler(mIfaceName, + mWifiMonitor.deregisterHandler(getIfaceName(), WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); } } @@ -190,7 +190,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call if (!allFreqs.isEmpty()) { freqs = allFreqs.getScanFreqs(); success = mWifiNative.scan( - mIfaceName, settings.scanType, freqs, hiddenNetworkSSIDSet); + getIfaceName(), settings.scanType, freqs, hiddenNetworkSSIDSet); if (!success) { Log.e(TAG, "Failed to start scan, freqs=" + freqs); } @@ -321,7 +321,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call // got a scan before we started scanning or after scan was canceled return; } - mNativePnoScanResults = mWifiNative.getPnoScanResults(mIfaceName); + mNativePnoScanResults = mWifiNative.getPnoScanResults(getIfaceName()); List<ScanResult> hwPnoScanResults = new ArrayList<>(); int numFilteredScanResults = 0; for (int i = 0; i < mNativePnoScanResults.size(); ++i) { @@ -373,7 +373,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call return; } - mNativeScanResults = mWifiNative.getScanResults(mIfaceName); + mNativeScanResults = mWifiNative.getScanResults(getIfaceName()); List<ScanResult> singleScanResults = new ArrayList<>(); int numFilteredScanResults = 0; for (int i = 0; i < mNativeScanResults.size(); ++i) { @@ -419,11 +419,11 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call } private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) { - return mWifiNative.startPnoScan(mIfaceName, pnoSettings); + return mWifiNative.startPnoScan(getIfaceName(), pnoSettings); } private void stopHwPnoScan() { - mWifiNative.stopPnoScan(mIfaceName); + mWifiNative.stopPnoScan(getIfaceName()); } /** @@ -498,6 +498,16 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call }).collect(Collectors.toList()); ScanResultUtil.dumpScanResults(pw, pnoScanResults, nowMs); } + pw.println("Latest native scan results IEs:"); + if (mNativeScanResults != null) { + for (ScanDetail detail : mNativeScanResults) { + if (detail.getInformationElementRawData() != null) { + pw.println(NativeUtil.hexStringFromByteArray( + detail.getInformationElementRawData())); + } + } + } + pw.println(""); } } diff --git a/service/java/com/android/server/wifi/util/ApConfigUtil.java b/service/java/com/android/server/wifi/util/ApConfigUtil.java index c3a4c0c95f..2ca0641c36 100644 --- a/service/java/com/android/server/wifi/util/ApConfigUtil.java +++ b/service/java/com/android/server/wifi/util/ApConfigUtil.java @@ -44,6 +44,7 @@ public class ApConfigUtil { /** * Convert frequency to channel. + * Note: the utility does not perform any regulatory domain compliance. * @param frequency frequency to convert * @return channel number associated with given frequency, -1 if no match */ @@ -52,7 +53,7 @@ public class ApConfigUtil { return (frequency - 2412) / 5 + 1; } else if (frequency == 2484) { return 14; - } else if (frequency >= 5170 && frequency <= 5825) { + } else if (frequency >= 5170 && frequency <= 5865) { /* DFS is included. */ return (frequency - 5170) / 5 + 34; } diff --git a/service/java/com/android/server/wifi/util/EncryptedData.java b/service/java/com/android/server/wifi/util/EncryptedData.java index 91342d3356..0b29099f8a 100644 --- a/service/java/com/android/server/wifi/util/EncryptedData.java +++ b/service/java/com/android/server/wifi/util/EncryptedData.java @@ -29,10 +29,11 @@ public class EncryptedData { private final byte[] mIv; public EncryptedData(byte[] encryptedData, byte[] iv) { - Preconditions.checkNotNull(encryptedData, iv); - Preconditions.checkState(encryptedData.length == ENCRYPTED_DATA_LENGTH, + Preconditions.checkNotNull(encryptedData); + Preconditions.checkNotNull(iv); + Preconditions.checkArgument(encryptedData.length == ENCRYPTED_DATA_LENGTH, "encryptedData.length=" + encryptedData.length); - Preconditions.checkState(iv.length == IV_LENGTH, "iv.length=" + iv.length); + Preconditions.checkArgument(iv.length == IV_LENGTH, "iv.length=" + iv.length); mEncryptedData = encryptedData; mIv = iv; } diff --git a/service/java/com/android/server/wifi/util/Environment.java b/service/java/com/android/server/wifi/util/Environment.java new file mode 100644 index 0000000000..9ce90f8f05 --- /dev/null +++ b/service/java/com/android/server/wifi/util/Environment.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 java.io.File; + +/** + * Provides access to environment variables. + * + * Note: @hide methods copied from android.os.Environment + */ +public class Environment { + /** + * Get data/misc directory + */ + public static File getDataMiscDirectory() { + return new File(android.os.Environment.getDataDirectory(), "misc"); + } + + /** + * Get data/misc_ce/<userId> directory + */ + public static File getDataMiscCeDirectory(int userId) { + return buildPath(android.os.Environment.getDataDirectory(), "misc_ce", + String.valueOf(userId)); + } + + /** + * Append path segments to given base path, returning result. + */ + public static File buildPath(File base, String... segments) { + File cur = base; + for (String segment : segments) { + if (cur == null) { + cur = new File(segment); + } else { + cur = new File(cur, segment); + } + } + return cur; + } + +} diff --git a/service/java/com/android/server/wifi/util/ExternalCallbackTracker.java b/service/java/com/android/server/wifi/util/ExternalCallbackTracker.java index 8f92da4315..225e68ac44 100644 --- a/service/java/com/android/server/wifi/util/ExternalCallbackTracker.java +++ b/service/java/com/android/server/wifi/util/ExternalCallbackTracker.java @@ -17,6 +17,7 @@ package com.android.server.wifi.util; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -72,11 +73,11 @@ public class ExternalCallbackTracker<T> { * @return an instance of {@link ExternalCallbackHolder} if there are no failures, otherwise * null. */ - public static <T> ExternalCallbackHolder createAndLinkToDeath( + public static <T> ExternalCallbackHolder<T> createAndLinkToDeath( @NonNull IBinder binder, @NonNull T callbackObject, @NonNull DeathCallback deathCallback) { ExternalCallbackHolder<T> externalCallback = - new ExternalCallbackHolder<T>(binder, callbackObject, deathCallback); + new ExternalCallbackHolder<>(binder, callbackObject, deathCallback); try { binder.linkToDeath(externalCallback, 0); } catch (RemoteException e) { @@ -131,8 +132,9 @@ public class ExternalCallbackTracker<T> { }); }); if (externalCallback == null) return false; - if (mCallbacks.containsKey(callbackIdentifier) && remove(callbackIdentifier)) { + if (mCallbacks.containsKey(callbackIdentifier)) { Log.d(TAG, "Replacing callback " + callbackIdentifier); + remove(callbackIdentifier); } mCallbacks.put(callbackIdentifier, externalCallback); if (mCallbacks.size() > NUM_CALLBACKS_WTF_LIMIT) { @@ -145,16 +147,16 @@ public class ExternalCallbackTracker<T> { /** * Remove a callback object to tracker. - * @return true on success, false on failure. + * @return Removed object instance on success, null on failure. */ - public boolean remove(int callbackIdentifier) { + public @Nullable T remove(int callbackIdentifier) { ExternalCallbackHolder<T> externalCallback = mCallbacks.remove(callbackIdentifier); if (externalCallback == null) { Log.w(TAG, "Unknown external callback " + callbackIdentifier); - return false; + return null; } externalCallback.reset(); - return true; + return externalCallback.getCallback(); } /** diff --git a/service/java/com/android/server/wifi/util/FileUtils.java b/service/java/com/android/server/wifi/util/FileUtils.java new file mode 100644 index 0000000000..07b1155c33 --- /dev/null +++ b/service/java/com/android/server/wifi/util/FileUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * Utility methods useful for working with files. + * + * Note: @hide methods copied from android.os.FileUtils + */ +public final class FileUtils { + private static final String TAG = "FileUtils"; + + /** + * Set owner and mode of of given path. + * + * @param mode to apply through {@code chmod} + * @param uid to apply through {@code chown}, or -1 to leave unchanged + * @param gid to apply through {@code chown}, or -1 to leave unchanged + * @return 0 on success, otherwise errno. + */ + public static int setPermissions(String path, int mode, int uid, int gid) { + try { + Os.chmod(path, mode); + } catch (ErrnoException e) { + Log.w(TAG, "Failed to chmod(" + path + "): " + e); + return e.errno; + } + + if (uid >= 0 || gid >= 0) { + try { + Os.chown(path, uid, gid); + } catch (ErrnoException e) { + Log.w(TAG, "Failed to chown(" + path + "): " + e); + return e.errno; + } + } + + return 0; + } + + /** + * Writes the bytes given in {@code content} to the file whose absolute path + * is {@code filename}. + */ + public static void bytesToFile(String filename, byte[] content) throws IOException { + try (FileOutputStream fos = new FileOutputStream(filename)) { + fos.write(content); + } + } + + /** + * Writes string to file. Basically same as "echo -n $string > $filename" + * + * @param filename + * @param string + * @throws IOException + */ + public static void stringToFile(String filename, String string) throws IOException { + bytesToFile(filename, string.getBytes(StandardCharsets.UTF_8)); + } + +} diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java index 5a55750d69..61b600b97d 100644 --- a/service/java/com/android/server/wifi/util/InformationElementUtil.java +++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java @@ -151,73 +151,148 @@ public class InformationElementUtil { } public static class HtOperation { - public int secondChannelOffset = 0; + private static final int HT_OPERATION_IE_LEN = 22; + private boolean mPresent = false; + private int mSecondChannelOffset = 0; + /** + * returns if HT Operation IE present in the message. + */ + public boolean isPresent() { + return mPresent; + } + + /** + * Returns channel width if it is 20 or 40MHz + * Results will be invalid if channel width greater than 40MHz + * So caller should only call this method if VHT Operation IE is not present, + * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. + */ public int getChannelWidth() { - if (secondChannelOffset != 0) { - return 1; + if (mSecondChannelOffset != 0) { + return ScanResult.CHANNEL_WIDTH_40MHZ; } else { - return 0; + return ScanResult.CHANNEL_WIDTH_20MHZ; } } + /** + * Returns channel Center frequency (for 20/40 MHz channels only) + * Results will be invalid for larger channel width, + * So, caller should only call this method if VHT Operation IE is not present, + * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. + */ public int getCenterFreq0(int primaryFrequency) { - //40 MHz - if (secondChannelOffset != 0) { - if (secondChannelOffset == 1) { + if (mSecondChannelOffset != 0) { + //40 MHz + if (mSecondChannelOffset == 1) { return primaryFrequency + 10; - } else if (secondChannelOffset == 3) { + } else if (mSecondChannelOffset == 3) { return primaryFrequency - 10; } else { - Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset); + Log.e("HtOperation", "Error on secondChannelOffset: " + mSecondChannelOffset); return 0; } } else { - return 0; + //20 MHz + return primaryFrequency; } } + /** + * Parse the HT Operation IE to read the fields of interest. + */ public void from(InformationElement ie) { if (ie.id != InformationElement.EID_HT_OPERATION) { throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id); } - secondChannelOffset = ie.bytes[1] & 0x3; + if (ie.bytes.length < HT_OPERATION_IE_LEN) { + throw new IllegalArgumentException("Invalid HT_OPERATION len: " + ie.bytes.length); + } + mPresent = true; + mSecondChannelOffset = ie.bytes[1] & 0x3; } } public static class VhtOperation { - public int channelMode = 0; - public int centerFreqIndex1 = 0; - public int centerFreqIndex2 = 0; + private static final int VHT_OPERATION_IE_LEN = 5; + private boolean mPresent = false; + private int mChannelMode = 0; + private int mCenterFreqIndex1 = 0; + private int mCenterFreqIndex2 = 0; - public boolean isValid() { - return channelMode != 0; + /** + * returns if VHT Operation IE present in the message. + */ + public boolean isPresent() { + return mPresent; } + /** + * Returns channel width if it is above 40MHz, + * otherwise, returns {@link ScanResult.UNSPECIFIED} to indicate that + * channel width should be obtained from the HT Operation IE via + * HtOperation.getChannelWidth(). + */ public int getChannelWidth() { - return channelMode + 1; + if (mChannelMode == 0) { + // 20 or 40MHz + return ScanResult.UNSPECIFIED; + } else if (mCenterFreqIndex2 == 0) { + // No secondary channel + return ScanResult.CHANNEL_WIDTH_80MHZ; + } else if (Math.abs(mCenterFreqIndex2 - mCenterFreqIndex1) == 8) { + // Primary and secondary channels adjacent + return ScanResult.CHANNEL_WIDTH_160MHZ; + } else { + // Primary and secondary channels not adjacent + return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; + } } + /** + * Returns center frequency of primary channel (if channel width greater than 40MHz), + * otherwise, it returns zero to indicate that center frequency should be obtained from + * the HT Operation IE via HtOperation.getCenterFreq0(). + */ public int getCenterFreq0() { - //convert channel index to frequency in MHz, channel 36 is 5180MHz - return (centerFreqIndex1 - 36) * 5 + 5180; + if (mCenterFreqIndex1 == 0 || mChannelMode == 0) { + return 0; + } else { + //convert channel index to frequency in MHz, channel 36 is 5180MHz + return (mCenterFreqIndex1 - 36) * 5 + 5180; + } } + /** + * Returns center frequency of secondary channel if exists (channel width greater than + * 40MHz), otherwise, it returns zero. + * Note that the secondary channel center frequency only applies to 80+80 or 160 MHz + * channels. + */ public int getCenterFreq1() { - if (channelMode > 1) { //160MHz - return (centerFreqIndex2 - 36) * 5 + 5180; - } else { + if (mCenterFreqIndex2 == 0 || mChannelMode == 0) { return 0; + } else { + //convert channel index to frequency in MHz, channel 36 is 5180MHz + return (mCenterFreqIndex2 - 36) * 5 + 5180; } } + /** + * Parse the VHT Operation IE to read the fields of interest. + */ public void from(InformationElement ie) { if (ie.id != InformationElement.EID_VHT_OPERATION) { throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id); } - channelMode = ie.bytes[0] & Constants.BYTE_MASK; - centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; - centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; + if (ie.bytes.length < VHT_OPERATION_IE_LEN) { + throw new IllegalArgumentException("Invalid VHT_OPERATION len: " + ie.bytes.length); + } + mPresent = true; + mChannelMode = ie.bytes[0] & Constants.BYTE_MASK; + mCenterFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; + mCenterFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; } } @@ -395,6 +470,7 @@ public class InformationElementUtil { */ public static class Capabilities { private static final int CAP_ESS_BIT_OFFSET = 0; + private static final int CAP_IBSS_BIT_OFFSET = 1; private static final int CAP_PRIVACY_BIT_OFFSET = 4; private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000; @@ -416,6 +492,7 @@ public class InformationElementUtil { private static final int RSN_AKM_FT_SAE = 0x09ac0f00; private static final int RSN_AKM_OWE = 0x12ac0f00; private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00; + private static final int RSN_OSEN = 0x019a6f50; private static final int WPA_CIPHER_NONE = 0x00f25000; private static final int WPA_CIPHER_TKIP = 0x02f25000; @@ -432,6 +509,7 @@ public class InformationElementUtil { public ArrayList<ArrayList<Integer>> pairwiseCipher; public ArrayList<Integer> groupCipher; public boolean isESS; + public boolean isIBSS; public boolean isPrivacy; public boolean isWPS; @@ -514,6 +592,9 @@ public class InformationElementUtil { case RSN_AKM_EAP_SUITE_B_192: rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192); break; + case RSN_OSEN: + rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); + break; default: // do nothing break; @@ -678,6 +759,7 @@ public class InformationElementUtil { return; } isESS = beaconCap.get(CAP_ESS_BIT_OFFSET); + isIBSS = beaconCap.get(CAP_IBSS_BIT_OFFSET); isPrivacy = beaconCap.get(CAP_PRIVACY_BIT_OFFSET); for (InformationElement ie : ies) { if (ie.id == InformationElement.EID_RSN) { @@ -739,6 +821,8 @@ public class InformationElementUtil { return "WPA"; case ScanResult.PROTOCOL_RSN: return "RSN"; + case ScanResult.PROTOCOL_OSEN: + return "OSEN"; default: return "?"; } @@ -770,6 +854,8 @@ public class InformationElementUtil { return "FT/SAE"; case ScanResult.KEY_MGMT_EAP_SUITE_B_192: return "EAP_SUITE_B_192"; + case ScanResult.KEY_MGMT_OSEN: + return "OSEN"; default: return "?"; } @@ -814,6 +900,9 @@ public class InformationElementUtil { if (isESS) { capabilities.append("[ESS]"); } + if (isIBSS) { + capabilities.append("[IBSS]"); + } if (isWPS) { capabilities.append("[WPS]"); } diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java index 4af40ddf20..a80c18cf66 100644 --- a/service/java/com/android/server/wifi/util/TelephonyUtil.java +++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java @@ -22,6 +22,7 @@ import android.net.wifi.WifiEnterpriseConfig; import android.telephony.ImsiEncryptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Base64; import android.util.Log; import android.util.Pair; @@ -728,6 +729,45 @@ public class TelephonyUtil { * Returns true if at least one SIM is present on the device, false otherwise. */ public static boolean isSimPresent(@Nonnull SubscriptionManager sm) { - return sm.getActiveSubscriptionIdList().length > 0; + return !sm.getActiveSubscriptionInfoList().isEmpty(); + } + + /** + * Decorates a pseudonym with the NAI realm, in case it wasn't provided by the server + * + * @param tm TelephonyManager instance + * @param pseudonym The pseudonym (temporary identity) provided by the server + * @return pseudonym@realm which is based on current MCC/MNC, {@code null} if SIM is + * not ready or absent. + */ + public static String decoratePseudonymWith3GppRealm(@NonNull TelephonyManager tm, + String pseudonym) { + if (tm == null || TextUtils.isEmpty(pseudonym)) { + return null; + } + if (pseudonym.contains("@")) { + // Pseudonym is already decorated + return pseudonym; + } + TelephonyManager defaultDataTm = tm.createForSubscriptionId( + SubscriptionManager.getDefaultDataSubscriptionId()); + if (defaultDataTm.getSimState() != TelephonyManager.SIM_STATE_READY) { + return null; + } + String mccMnc = defaultDataTm.getSimOperator(); + if (mccMnc == null || mccMnc.isEmpty()) { + return null; + } + + // Extract mcc & mnc from mccMnc + String mcc = mccMnc.substring(0, 3); + String mnc = mccMnc.substring(3); + + if (mnc.length() == 2) { + mnc = "0" + mnc; + } + + String realm = String.format(THREE_GPP_NAI_REALM_FORMAT, mnc, mcc); + return String.format("%s@%s", pseudonym, realm); } } diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java index b1ceaf37af..6e50743581 100644 --- a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java +++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java @@ -17,25 +17,24 @@ package com.android.server.wifi.util; import android.Manifest; +import android.annotation.Nullable; import android.app.AppOpsManager; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; import android.location.LocationManager; import android.net.NetworkStack; import android.os.Binder; import android.os.Build; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiLog; -import java.util.List; - /** * A wifi permissions utility assessing permissions * for getting scan results by a package. @@ -67,45 +66,8 @@ public class WifiPermissionsUtil { * @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; - } - } - - /** - * Checks if the app has the permission to change Wi-Fi network configuration or not. - * - * @param uid uid of the app. - * @return true if the app does have the permission, false otherwise. - */ - public boolean checkChangePermission(int uid) { - try { - int permission = mWifiPermissionsWrapper.getChangeWifiConfigPermission(uid); - return (permission == PackageManager.PERMISSION_GRANTED); - } catch (RemoteException e) { - mLog.err("Error checking for permission: %").r(e.getMessage()).flush(); - return false; - } - } - - /** - * Checks if the app has the permission to access Wi-Fi state or not. - * - * @param uid uid of the app. - * @return true if the app does have the permission, false otherwise. - */ - public boolean checkWifiAccessPermission(int uid) { - try { - int permission = mWifiPermissionsWrapper.getAccessWifiStatePermission(uid); - return (permission == PackageManager.PERMISSION_GRANTED); - } catch (RemoteException e) { - mLog.err("Error checking for permission: %").r(e.getMessage()).flush(); - return false; - } + int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid); + return permission == PackageManager.PERMISSION_GRANTED; } /** @@ -128,7 +90,8 @@ public class WifiPermissionsUtil { long ident = Binder.clearCallingIdentity(); try { if (mContext.getPackageManager().getApplicationInfoAsUser( - packageName, 0, UserHandle.getUserId(callingUid)).targetSdkVersion + packageName, 0, + UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion < versionCode) { return true; } @@ -168,11 +131,15 @@ public class WifiPermissionsUtil { // Always checking FINE - even if will not enforce. This will record the request for FINE // so that a location request by the app is surfaced to the user. - boolean isAppOpAllowed = noteAppOpAllowed(AppOpsManager.OP_FINE_LOCATION, pkgName, uid); - if (!isAppOpAllowed && coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) { - isAppOpAllowed = noteAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid); + boolean isFineLocationAllowed = noteAppOpAllowed( + AppOpsManager.OPSTR_FINE_LOCATION, pkgName, uid); + if (isFineLocationAllowed) { + return true; + } + if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) { + return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, uid); } - return isAppOpAllowed; + return false; } /** @@ -187,7 +154,6 @@ public class WifiPermissionsUtil { } } - /** * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION * and a corresponding app op is allowed for this package and uid. @@ -195,7 +161,7 @@ public class WifiPermissionsUtil { * @param pkgName PackageName of the application requesting access * @param uid The uid of the package * @param hideFromAppOps True to invoke {@link AppOpsManager#checkOp(int, int, String)}, false - * to invoke {@link AppOpsManager#noteOp(int, int, String)}. + * to invoke {@link AppOpsManager#noteOp(String, int, String, String)}. */ private boolean checkCallersFineLocationPermission(String pkgName, int uid, boolean hideFromAppOps) { @@ -207,15 +173,10 @@ public class WifiPermissionsUtil { } if (hideFromAppOps) { // Don't note the operation, just check if the app is allowed to perform the operation. - if (!checkAppOpAllowed(AppOpsManager.OP_FINE_LOCATION, pkgName, uid)) { - return false; - } + return checkAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, uid); } else { - if (!noteAppOpAllowed(AppOpsManager.OP_FINE_LOCATION, pkgName, uid)) { - return false; - } + return noteAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, uid); } - return true; } /** @@ -327,7 +288,7 @@ public class WifiPermissionsUtil { try { checkPackage(uid, pkgName); } catch (SecurityException se) { - Slog.e(TAG, "Package check exception - " + se); + Log.e(TAG, "Package check exception - " + se); return false; } @@ -338,7 +299,7 @@ public class WifiPermissionsUtil { // Location mode must be enabled if needed. if (needLocationModeEnabled && !isLocationModeEnabled()) { - Slog.e(TAG, "Location mode is disabled for the device"); + Log.e(TAG, "Location mode is disabled for the device"); return false; } @@ -346,7 +307,7 @@ public class WifiPermissionsUtil { // location information. if (!checkCallersLocationPermission(pkgName, uid, /* coarseForTargetSdkLessThanQ */ false)) { - Slog.e(TAG, "UID " + uid + " has no location permission"); + Log.e(TAG, "UID " + uid + " has no location permission"); return false; } return true; @@ -381,7 +342,7 @@ public class WifiPermissionsUtil { * and package. */ private boolean isScanAllowedbyApps(String pkgName, int uid) { - return noteAppOpAllowed(AppOpsManager.OP_WIFI_SCAN, pkgName, uid); + return noteAppOpAllowed(AppOpsManager.OPSTR_WIFI_SCAN, pkgName, uid); } /** @@ -398,27 +359,19 @@ public class WifiPermissionsUtil { * 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; + UserHandle currentUser = UserHandle.of(mWifiPermissionsWrapper.getCurrentUser()); + UserHandle callingUser = UserHandle.getUserHandleForUid(uid); + return currentUser.equals(callingUser) + || mUserManager.isSameProfileGroup(currentUser, callingUser); } - private boolean noteAppOpAllowed(int op, String pkgName, int uid) { - return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; + private boolean noteAppOpAllowed(String op, String pkgName, int uid) { + // TODO moltmann: Set correct featureId + return mAppOps.noteOp(op, uid, pkgName, null, null) == AppOpsManager.MODE_ALLOWED; } - private boolean checkAppOpAllowed(int op, String pkgName, int uid) { - return mAppOps.checkOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; + private boolean checkAppOpAllowed(String op, String pkgName, int uid) { + return mAppOps.unsafeCheckOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; } private boolean retrieveLocationManagerIfNecessary() { @@ -508,8 +461,8 @@ public class WifiPermissionsUtil { * Returns true if the |callingUid|/\callingPackage| holds SYSTEM_ALERT_WINDOW permission. */ public boolean checkSystemAlertWindowPermission(int callingUid, String callingPackage) { - final int mode = mAppOps.noteOp( - AppOpsManager.OP_SYSTEM_ALERT_WINDOW, callingUid, callingPackage); + final int mode = mAppOps.noteOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, callingUid, + callingPackage, null, null); if (mode == AppOpsManager.MODE_DEFAULT) { return mWifiPermissionsWrapper.getUidPermission( Manifest.permission.SYSTEM_ALERT_WINDOW, callingUid) @@ -517,4 +470,75 @@ public class WifiPermissionsUtil { } return mode == AppOpsManager.MODE_ALLOWED; } + + private static DevicePolicyManager retrieveDevicePolicyManagerFromContext(Context context) { + DevicePolicyManager devicePolicyManager = + context.getSystemService(DevicePolicyManager.class); + if (devicePolicyManager == null + && context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_DEVICE_ADMIN)) { + Log.w(TAG, "Error retrieving DPM service"); + } + return devicePolicyManager; + } + + private DevicePolicyManager retrieveDevicePolicyManagerFromUserContext(int uid) { + Context userContext = null; + try { + userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0, + UserHandle.getUserHandleForUid(uid)); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unknown package name"); + return null; + } + if (userContext == null) { + Log.e(TAG, "Unable to retrieve user context for " + uid); + return null; + } + return retrieveDevicePolicyManagerFromContext(userContext); + } + + /** + * Returns true if the |callingUid|/\callingPackage| is the device owner. + */ + public boolean isDeviceOwner(int uid, @Nullable String packageName) { + // Cannot determine if the app is DO/PO if packageName is null. So, will return false to be + // safe. + if (packageName == null) { + Log.e(TAG, "isDeviceOwner: packageName is null, returning false"); + return false; + } + DevicePolicyManager devicePolicyManager = + retrieveDevicePolicyManagerFromContext(mContext); + if (devicePolicyManager == null) return false; + long ident = Binder.clearCallingIdentity(); + UserHandle deviceOwnerUser = null; + ComponentName deviceOwnerComponent = null; + try { + deviceOwnerUser = devicePolicyManager.getDeviceOwnerUser(); + deviceOwnerComponent = devicePolicyManager.getDeviceOwnerComponentOnAnyUser(); + } finally { + Binder.restoreCallingIdentity(ident); + } + // no device owner + if (deviceOwnerUser == null || deviceOwnerComponent == null) return false; + return deviceOwnerUser.equals(UserHandle.getUserHandleForUid(uid)) + && deviceOwnerComponent.getPackageName().equals(packageName); + } + + /** + * Returns true if the |callingUid|/\callingPackage| is the profile owner. + */ + public boolean isProfileOwner(int uid, @Nullable String packageName) { + // Cannot determine if the app is DO/PO if packageName is null. So, will return false to be + // safe. + if (packageName == null) { + Log.e(TAG, "isProfileOwner: packageName is null, returning false"); + return false; + } + DevicePolicyManager devicePolicyManager = + retrieveDevicePolicyManagerFromUserContext(uid); + if (devicePolicyManager == null) return false; + return devicePolicyManager.isProfileOwnerApp(packageName); + } } diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java index 18e08d260b..52ea1c0f13 100644 --- a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java +++ b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java @@ -18,15 +18,7 @@ package com.android.server.wifi.util; import android.Manifest; 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 @@ -45,44 +37,14 @@ public class WifiPermissionsWrapper { } /** - * 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 + * Determine if a UID has a permission. + * @param permissionType 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); + // We don't care about pid, pass in -1 + return mContext.checkPermission(permissionType, -1, uid); } /** @@ -90,35 +52,9 @@ public class WifiPermissionsWrapper { * * @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); - } - - /** - * Determines if the caller has the change wifi config permission. - * - * @param uid to check the permission for - * @return int representation of success or denied - * @throws RemoteException - */ - public int getChangeWifiConfigPermission(int uid) throws RemoteException { - return AppGlobals.getPackageManager().checkUidPermission( - Manifest.permission.CHANGE_WIFI_STATE, uid); - } - - /** - * Determines if the caller has the access wifi state permission. - * - * @param uid to check the permission for - * @return int representation of success or denied - * @throws RemoteException */ - public int getAccessWifiStatePermission(int uid) throws RemoteException { - return AppGlobals.getPackageManager().checkUidPermission( - Manifest.permission.ACCESS_WIFI_STATE, uid); + public int getOverrideWifiConfigPermission(int uid) { + return getUidPermission(android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid); } /** @@ -126,10 +62,8 @@ public class WifiPermissionsWrapper { * * @param uid to check the permission for * @return int representation of success or denied - * @throws RemoteException */ - public int getLocalMacAddressPermission(int uid) throws RemoteException { - return AppGlobals.getPackageManager().checkUidPermission( - Manifest.permission.LOCAL_MAC_ADDRESS, uid); + public int getLocalMacAddressPermission(int uid) { + return getUidPermission(Manifest.permission.LOCAL_MAC_ADDRESS, uid); } } diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java index 188d3b5c72..e1c48785aa 100644 --- a/service/java/com/android/server/wifi/util/XmlUtil.java +++ b/service/java/com/android/server/wifi/util/XmlUtil.java @@ -348,6 +348,7 @@ public class XmlUtil { public static final String XML_TAG_ROAMING_CONSORTIUM_OIS = "RoamingConsortiumOIs"; public static final String XML_TAG_RANDOMIZED_MAC_ADDRESS = "RandomizedMacAddress"; public static final String XML_TAG_MAC_RANDOMIZATION_SETTING = "MacRandomizationSetting"; + public static final String XML_TAG_SAE_PASSWORD_ID_KEY = "SaePasswordId"; /** * Write WepKeys to the XML stream. @@ -390,6 +391,7 @@ public class XmlUtil { 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); + XmlUtil.writeNextValue(out, XML_TAG_SAE_PASSWORD_ID_KEY, configuration.saePasswordId); writeWepKeysToXml(out, configuration.wepKeys); XmlUtil.writeNextValue(out, XML_TAG_WEP_TX_KEY_INDEX, configuration.wepTxKeyIndex); XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, configuration.hiddenSSID); @@ -541,6 +543,9 @@ public class XmlUtil { case XML_TAG_PRE_SHARED_KEY: configuration.preSharedKey = (String) value; break; + case XML_TAG_SAE_PASSWORD_ID_KEY: + configuration.saePasswordId = (String) value; + break; case XML_TAG_WEP_KEYS: populateWepKeysFromXmlValue(value, configuration.wepKeys); break; @@ -808,10 +813,9 @@ public class XmlUtil { 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); + RouteInfo route = new RouteInfo(null, gateway, null, RouteInfo.RTN_UNICAST); if (route.isIPv4Default()) { staticIpConfiguration.gateway = gateway; } else { @@ -1017,6 +1021,7 @@ public class XmlUtil { 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"; + public static final String XML_TAG_OCSP = "Ocsp"; /** * Write the WifiEnterpriseConfig data elements from the provided config to the XML @@ -1055,6 +1060,7 @@ public class XmlUtil { 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()); + XmlUtil.writeNextValue(out, XML_TAG_OCSP, enterpriseConfig.getOcsp()); } /** @@ -1124,6 +1130,9 @@ public class XmlUtil { enterpriseConfig.setFieldValue( WifiEnterpriseConfig.CA_PATH_KEY, (String) value); break; + case XML_TAG_OCSP: + enterpriseConfig.setOcsp((int) value); + break; case XML_TAG_EAP_METHOD: enterpriseConfig.setEapMethod((int) value); break; diff --git a/service/java/com/android/server/wifi/wificond/ChannelSettings.java b/service/java/com/android/server/wifi/wificond/ChannelSettings.java deleted file mode 100644 index 7b20983b6a..0000000000 --- a/service/java/com/android/server/wifi/wificond/ChannelSettings.java +++ /dev/null @@ -1,95 +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.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 deleted file mode 100644 index 1a96f2557a..0000000000 --- a/service/java/com/android/server/wifi/wificond/HiddenNetwork.java +++ /dev/null @@ -1,87 +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.wificond; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; - -/** - * 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 Arrays.equals(ssid, network.ssid); - } - - /** override hash code */ - @Override - public int hashCode() { - return Arrays.hashCode(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(); - 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 deleted file mode 100644 index d7bfed8f0e..0000000000 --- a/service/java/com/android/server/wifi/wificond/NativeScanResult.java +++ /dev/null @@ -1,114 +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.wificond; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -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 ArrayList<RadioChainInfo> radioChainInfos; - - /** 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); - out.writeTypedList(radioChainInfos); - } - - /** 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); - result.radioChainInfos = new ArrayList<>(); - in.readTypedList(result.radioChainInfos, RadioChainInfo.CREATOR); - return result; - } - - @Override - public NativeScanResult[] newArray(int size) { - return new NativeScanResult[size]; - } - }; -} diff --git a/service/java/com/android/server/wifi/wificond/PnoNetwork.java b/service/java/com/android/server/wifi/wificond/PnoNetwork.java deleted file mode 100644 index 6bcad06ce0..0000000000 --- a/service/java/com/android/server/wifi/wificond/PnoNetwork.java +++ /dev/null @@ -1,95 +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.wificond; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; -import java.util.Objects; - -/** - * PnoNetwork for wificond - * - * @hide - */ -public class PnoNetwork implements Parcelable { - - public boolean isHidden; - public byte[] ssid; - public int[] frequencies; - - /** 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 Arrays.equals(ssid, network.ssid) - && Arrays.equals(frequencies, network.frequencies) - && isHidden == network.isHidden; - } - - /** override hash code */ - @Override - public int hashCode() { - return Objects.hash( - isHidden, - Arrays.hashCode(ssid), - Arrays.hashCode(frequencies)); - } - - /** 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); - out.writeIntArray(frequencies); - } - - /** 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(); - result.frequencies = in.createIntArray(); - 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 deleted file mode 100644 index 51a7cc19b3..0000000000 --- a/service/java/com/android/server/wifi/wificond/PnoSettings.java +++ /dev/null @@ -1,101 +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.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/RadioChainInfo.java b/service/java/com/android/server/wifi/wificond/RadioChainInfo.java deleted file mode 100644 index eeb9487e45..0000000000 --- a/service/java/com/android/server/wifi/wificond/RadioChainInfo.java +++ /dev/null @@ -1,100 +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.wificond; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Objects; - -/** - * RadioChainInfo for wificond - * - * @hide - */ -public class RadioChainInfo implements Parcelable { - private static final String TAG = "RadioChainInfo"; - - public int chainId; - public int level; - - - /** public constructor */ - public RadioChainInfo() { } - - public RadioChainInfo(int chainId, int level) { - this.chainId = chainId; - this.level = level; - } - - /** override comparator */ - @Override - public boolean equals(Object rhs) { - if (this == rhs) return true; - if (!(rhs instanceof RadioChainInfo)) { - return false; - } - RadioChainInfo chainInfo = (RadioChainInfo) rhs; - if (chainInfo == null) { - return false; - } - return chainId == chainInfo.chainId && level == chainInfo.level; - } - - /** override hash code */ - @Override - public int hashCode() { - return Objects.hash(chainId, level); - } - - - /** 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(chainId); - out.writeInt(level); - } - - /** implement Parcelable interface */ - public static final Parcelable.Creator<RadioChainInfo> CREATOR = - new Parcelable.Creator<RadioChainInfo>() { - /** - * Caller is responsible for providing a valid parcel. - */ - @Override - public RadioChainInfo createFromParcel(Parcel in) { - RadioChainInfo result = new RadioChainInfo(); - result.chainId = in.readInt(); - result.level = in.readInt(); - return result; - } - - @Override - public RadioChainInfo[] newArray(int size) { - return new RadioChainInfo[size]; - } - }; -} diff --git a/service/java/com/android/server/wifi/wificond/SingleScanSettings.java b/service/java/com/android/server/wifi/wificond/SingleScanSettings.java deleted file mode 100644 index ed1e41cdcf..0000000000 --- a/service/java/com/android/server/wifi/wificond/SingleScanSettings.java +++ /dev/null @@ -1,119 +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.wificond; - -import android.net.wifi.IWifiScannerImpl; -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 int scanType; - 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 scanType == settings.scanType - && channelSettings.equals(settings.channelSettings) - && hiddenNetworks.equals(settings.hiddenNetworks); - } - - /** override hash code */ - @Override - public int hashCode() { - return Objects.hash(scanType, channelSettings, hiddenNetworks); - } - - - /** implement Parcelable interface */ - @Override - public int describeContents() { - return 0; - } - - private static boolean isValidScanType(int scanType) { - return scanType == IWifiScannerImpl.SCAN_TYPE_LOW_SPAN - || scanType == IWifiScannerImpl.SCAN_TYPE_LOW_POWER - || scanType == IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; - } - - /** - * implement Parcelable interface - * |flags| is ignored. - */ - @Override - public void writeToParcel(Parcel out, int flags) { - if (!isValidScanType(scanType)) { - Log.wtf(TAG, "Invalid scan type " + scanType); - } - out.writeInt(scanType); - 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.scanType = in.readInt(); - if (!isValidScanType(result.scanType)) { - Log.wtf(TAG, "Invalid scan type " + result.scanType); - } - 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/proguard.flags b/service/proguard.flags new file mode 100644 index 0000000000..706449103a --- /dev/null +++ b/service/proguard.flags @@ -0,0 +1 @@ +# TBD diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml new file mode 100644 index 0000000000..83d6e5412b --- /dev/null +++ b/service/res/values/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Official label of the wifi stack --> + <string name="wifiAppLabel" product="default">System Wi-Fi</string> +</resources> diff --git a/service/tests/Android.bp b/service/tests/Android.bp new file mode 100644 index 0000000000..087691d89e --- /dev/null +++ b/service/tests/Android.bp @@ -0,0 +1,19 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 subdirectory makefiles +// ============================================================ +subdirs = [ + "wifitests", +] diff --git a/service/tests/Android.mk b/service/tests/Android.mk deleted file mode 100644 index 2027fefeb5..0000000000 --- a/service/tests/Android.mk +++ /dev/null @@ -1,21 +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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -# Include subdirectory makefiles -# ============================================================ -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/service/tests/wifitests/Android.bp b/service/tests/wifitests/Android.bp new file mode 100644 index 0000000000..fbad530e14 --- /dev/null +++ b/service/tests/wifitests/Android.bp @@ -0,0 +1,81 @@ +// 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. + +// Make test APK +// ============================================================ +android_test { + // TODO(rpius): Rename once all google3 config files are changed + // name: "WifiStackTests", + name: "FrameworksWifiTests", + + srcs: ["**/*.java"], + + // For coverage + jacoco: { + include_filter: [ + "com.android.server.wifi.**", + ], + exclude_filter: [ + ], + }, + + dxflags: ["--multi-dex"], + + // 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. + // TODO figure out if this is the correct thing to do, this seems to not be right + // since neither is declared a static java library. + static_libs: [ + "androidx.test.rules", + "hamcrest-library", + "mockito-target-extended-minus-junit4", + "frameworks-base-testutils", + "services", + "wifi-service", + "truth-prebuilt", + ], + + libs: [ + "android.test.runner", + "android.hidl.manager-V1.2-java", + "android.test.base", + "android.test.mock", + ], + + // These must be explicitly included because they are not normally accessible + // from apps. + jni_libs: [ + "libwifi-jni", + "libbase", + "libc++", + "ld-android", + "libdl_android", + "libcgrouprc", + "libcutils", + "libnativehelper", + "libprocessgroup", + "libutils", + "libvndksupport", + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + jarjar_rules: ":wifi-jarjar-rules", + + min_sdk_version: "29", + platform_apis: true, + privileged: true, + test_suites: ["device-tests"], +} diff --git a/service/tests/wifitests/Android.mk b/service/tests/wifitests/Android.mk deleted file mode 100644 index 8e48f0c7f5..0000000000 --- a/service/tests/wifitests/Android.mk +++ /dev/null @@ -1,123 +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. - -LOCAL_PATH:= $(call my-dir) - -# Make test APK -# ============================================================ -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -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 -# The list is a comma separated list of class names with * matching zero or more characters. -# Example: -# Input files: src/com/android/server/wifi/Test.java src/com/android/server/wifi/AnotherTest.java -# Generated exclude list: com.android.server.wifi.Test*,com.android.server.wifi.AnotherTest* - -# Filter all src files to just java files -local_java_files := $(filter %.java,$(LOCAL_SRC_FILES)) -# Transform java file names into full class names. -# This only works if the class name matches the file name and the directory structure -# matches the package. -local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files))) -# Convert class name list to jacoco exclude list -# This appends a * to all classes and replace the space separators with commas. -# 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.* - -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. -# TODO figure out if this is the correct thing to do, this seems to not be right -# since neither is declared a static java library. -LOCAL_STATIC_JAVA_LIBRARIES := \ - androidx.test.rules hamcrest-library \ - mockito-target-extended-minus-junit4 \ - frameworks-base-testutils \ - services \ - wifi-service \ - truth-prebuilt \ - -LOCAL_JAVA_LIBRARIES := \ - android.test.runner \ - android.hidl.manager-V1.2-java \ - android.test.base \ - android.test.mock - -# These must be explicitly included because they are not normally accessible -# from apps. -LOCAL_JNI_SHARED_LIBRARIES := \ - libcrypto \ - libwifi-service \ - libEGL \ - libGLESv2 \ - libaudioutils \ - libbacktrace \ - libbase \ - libbinder \ - libbinderthreadstate \ - libc++ \ - ld-android \ - libdl_android \ - libcamera_client \ - libcamera_metadata \ - libcgrouprc \ - libcutils \ - libexpat \ - libgui \ - libhardware \ - libandroidicu \ - libjsoncpp \ - liblzma \ - libmedia \ - libnativehelper \ - libnbaio \ - libnetutils \ - libnl \ - libpowermanager \ - libprocessgroup \ - libsonivox \ - libstagefright_foundation \ - libstdc++ \ - libsync \ - libwifi-system \ - libui \ - libunwindstack \ - libutils \ - libvndksupport \ - libdexmakerjvmtiagent \ - libstaticjvmtiagent - -ifdef WPA_SUPPLICANT_VERSION -LOCAL_JNI_SHARED_LIBRARIES += libwpa_client -endif - -LOCAL_PACKAGE_NAME := FrameworksWifiTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_COMPATIBILITY_SUITE := device-tests - -include $(BUILD_PACKAGE) diff --git a/service/tests/wifitests/coverage.sh b/service/tests/wifitests/coverage.sh index f9ea1fbb17..22c681439d 100755 --- a/service/tests/wifitests/coverage.sh +++ b/service/tests/wifitests/coverage.sh @@ -1,4 +1,6 @@ -#!/usr/bin/env bash +#!/bin/sh + +# A shell script to generate a coverage report for opt/net/wifi if [[ ! ($# == 1) ]]; then echo "$0: usage: coverage.sh OUTPUT_DIR" @@ -10,10 +12,22 @@ if [ -z $ANDROID_BUILD_TOP ]; then exit 1 fi -cd "$(dirname $0)" #cd to directory containing this script +# Make the output directory and get its full name +OUTPUT_DIR="$1" +mkdir -p $OUTPUT_DIR || exit 1 +OUTPUT_DIR="`(cd $OUTPUT_DIR && pwd)`" +BUILD_OUT_DIR=$OUTPUT_DIR/out +cd "$(dirname $0)" #cd to directory containing this script REPORTER_JAR=$ANDROID_HOST_OUT/framework/jacoco-cli.jar +if [ -f $REPORTER_JAR ]; then + echo "jacoco-cli.jar found, skipping uninstrumented build" +else + echo "Building jacoco cli and adb" + $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \ + MODULES-IN-system-core MODULES-IN-external-jacoco || exit 1 +fi OUTPUT_DIR=$1 @@ -26,21 +40,21 @@ COVERAGE_OUTPUT_FILE=$OUTPUT_DIR/wifi_coverage.ec set -e # fail early set -x # print commands -# build this module so we can run its tests, and -# build system/core so we can invoke `adb`, and -# build jacoco-cli.jar so we can generate the report -$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \ - EMMA_INSTRUMENT=true \ - EMMA_INSTRUMENT_FRAMEWORK=false \ - EMMA_INSTRUMENT_STATIC=true \ - ANDROID_COMPILE_WITH_JACK=false \ - SKIP_BOOT_JARS_CHECK=true \ - MODULES-IN-frameworks-opt-net-wifi-tests \ - MODULES-IN-system-core \ - MODULES-IN-external-jacoco \ - FrameworksWifiTests - -APK_NAME="$(ls -t $(find $OUT -name FrameworksWifiTests.apk) | head -n 1)" +bash <<END_OF_BUILD_SCRIPT || { exit 1; } + cd $ANDROID_BUILD_TOP + source build/make/envsetup.sh + tapas FrameworksWifiTests + export OUT_DIR=$BUILD_OUT_DIR + export TARGET_PRODUCT=$TARGET_PRODUCT + export EMMA_INSTRUMENT=true + export EMMA_INSTRUMENT_FRAMEWORK=false + export EMMA_INSTRUMENT_STATIC=true + export ANDROID_COMPILE_WITH_JACK=false + export SKIP_BOOT_JARS_CHECK=true + m +END_OF_BUILD_SCRIPT + +APK_NAME="$(ls -t $(find $BUILD_OUT_DIR -name FrameworksWifiTests.apk) | head -n 1)" adb root adb wait-for-device @@ -51,13 +65,12 @@ adb install -r -g "$APK_NAME" adb shell am instrument -e coverage true --no-hidden-api-checks -w 'com.android.server.wifi.test/com.android.server.wifi.CustomTestRunner' -mkdir -p $OUTPUT_DIR adb pull $REMOTE_COVERAGE_OUTPUT_FILE $COVERAGE_OUTPUT_FILE java -jar $REPORTER_JAR \ report \ - --classfiles $ANDROID_PRODUCT_OUT/../../common/obj/APPS/FrameworksWifiTests_intermediates/jacoco/report-resources/jacoco-report-classes.jar \ + --classfiles $BUILD_OUT_DIR/soong/.intermediates/frameworks/opt/net/wifi/service/wifi-service/android_common/javac/classes/ \ --html $OUTPUT_DIR \ --sourcefiles $ANDROID_BUILD_TOP/frameworks/opt/net/wifi/tests/wifitests/src \ --sourcefiles $ANDROID_BUILD_TOP/frameworks/opt/net/wifi/service/java \ diff --git a/service/tests/wifitests/runtests.sh b/service/tests/wifitests/runtests.sh index 6e831994a5..085098a94b 100755 --- a/service/tests/wifitests/runtests.sh +++ b/service/tests/wifitests/runtests.sh @@ -31,7 +31,7 @@ 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. -$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode MODULES-IN-frameworks-opt-net-wifi-tests +$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode MODULES-IN-frameworks-opt-net-wifi-tests-wifitests APK_NAME="$(ls -t $(find $OUT -name FrameworksWifiTests.apk) | head -n 1)" set -x # print commands diff --git a/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java b/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java index f52229c1dc..a8e4a8acca 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java @@ -20,11 +20,30 @@ import static com.android.server.wifi.ActiveModeManager.SCAN_NONE; import static com.android.server.wifi.ActiveModeManager.SCAN_WITHOUT_HIDDEN_NETWORKS; import static com.android.server.wifi.ActiveModeManager.SCAN_WITH_HIDDEN_NETWORKS; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; - +import static com.google.common.truth.Truth.assertThat; + +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.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockingDetails; +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.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; +import android.location.LocationManager; +import android.net.wifi.WifiClient; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.BatteryStats; @@ -33,7 +52,10 @@ import android.util.Log; import androidx.test.filters.SmallTest; +import com.android.internal.R; import com.android.internal.app.IBatteryStats; +import com.android.server.wifi.util.GeneralUtil; +import com.android.server.wifi.util.WifiPermissionsUtil; import org.junit.After; import org.junit.Before; @@ -46,39 +68,48 @@ import org.mockito.stubbing.Answer; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; /** * Unit tests for {@link com.android.server.wifi.ActiveModeWarden}. */ @SmallTest -public class ActiveModeWardenTest { +public class ActiveModeWardenTest extends WifiBaseTest { public static final String TAG = "WifiActiveModeWardenTest"; - private static final String CLIENT_MODE_STATE_STRING = "ClientModeActiveState"; - private static final String SCAN_ONLY_MODE_STATE_STRING = "ScanOnlyModeActiveState"; - private static final String WIFI_DISABLED_STATE_STRING = "WifiDisabledState"; + private static final String ENABLED_STATE_STRING = "EnabledState"; + private static final String DISABLED_STATE_STRING = "DisabledState"; + private static final String WIFI_IFACE_NAME = "mockWlan"; + private static final int TEST_WIFI_RECOVERY_DELAY_MS = 2000; + TestLooper mLooper; @Mock WifiInjector mWifiInjector; @Mock Context mContext; @Mock Resources mResources; @Mock WifiNative mWifiNative; @Mock WifiApConfigStore mWifiApConfigStore; - TestLooper mLooper; @Mock ClientModeManager mClientModeManager; - @Mock ScanOnlyModeManager mScanOnlyModeManager; @Mock SoftApManager mSoftApManager; @Mock DefaultModeManager mDefaultModeManager; @Mock IBatteryStats mBatteryStats; @Mock SelfRecovery mSelfRecovery; @Mock BaseWifiDiagnostics mWifiDiagnostics; @Mock ScanRequestProxy mScanRequestProxy; - ClientModeManager.Listener mClientListener; - ScanOnlyModeManager.Listener mScanOnlyListener; - ScanOnlyModeCallback mScanOnlyCallback = new ScanOnlyModeCallback(); - ClientModeCallback mClientModeCallback = new ClientModeCallback(); + @Mock ClientModeImpl mClientModeImpl; + @Mock FrameworkFacade mFacade; + @Mock WifiSettingsStore mSettingsStore; + @Mock WifiPermissionsUtil mWifiPermissionsUtil; + + ActiveModeManager.Listener mClientListener; + ActiveModeManager.Listener mSoftApListener; WifiManager.SoftApCallback mSoftApManagerCallback; + SoftApModeConfiguration mSoftApConfig; @Mock WifiManager.SoftApCallback mSoftApStateMachineCallback; + @Mock WifiManager.SoftApCallback mLohsStateMachineCallback; WifiNative.StatusListener mWifiNativeStatusListener; ActiveModeWarden mActiveModeWarden; @@ -95,36 +126,67 @@ public class ActiveModeWardenTest { MockitoAnnotations.initMocks(this); mLooper = new TestLooper(); - when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery); - when(mWifiInjector.getWifiDiagnostics()).thenReturn(mWifiDiagnostics); when(mWifiInjector.getScanRequestProxy()).thenReturn(mScanRequestProxy); when(mClientModeManager.getScanMode()).thenReturn(SCAN_WITH_HIDDEN_NETWORKS); when(mContext.getResources()).thenReturn(mResources); - when(mScanOnlyModeManager.getScanMode()).thenReturn(SCAN_WITHOUT_HIDDEN_NETWORKS); when(mSoftApManager.getScanMode()).thenReturn(SCAN_NONE); - when(mResources.getString( - eq(com.android.internal.R.string.wifi_localhotspot_configure_ssid_default))) + when(mResources.getString(R.string.wifi_localhotspot_configure_ssid_default)) .thenReturn("AndroidShare"); + when(mResources.getInteger(R.integer.config_wifi_framework_recovery_timeout_delay)) + .thenReturn(TEST_WIFI_RECOVERY_DELAY_MS); + + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + + doAnswer(new Answer<ClientModeManager>() { + public ClientModeManager answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + mClientListener = (ClientModeManager.Listener) args[0]; + return mClientModeManager; + } + }).when(mWifiInjector).makeClientModeManager(any(ActiveModeManager.Listener.class)); + doAnswer(new Answer<SoftApManager>() { + public SoftApManager answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + mSoftApListener = (ActiveModeManager.Listener) args[0]; + mSoftApManagerCallback = (WifiManager.SoftApCallback) args[1]; + mSoftApConfig = (SoftApModeConfiguration) args[2]; + return mSoftApManager; + } + }).when(mWifiInjector).makeSoftApManager(any(ActiveModeManager.Listener.class), + any(WifiManager.SoftApCallback.class), any()); mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); mLooper.dispatchAll(); verify(mWifiNative).registerStatusListener(mStatusListenerCaptor.capture()); mWifiNativeStatusListener = mStatusListenerCaptor.getValue(); mActiveModeWarden.registerSoftApCallback(mSoftApStateMachineCallback); - mActiveModeWarden.registerScanOnlyCallback(mScanOnlyCallback); - mActiveModeWarden.registerClientModeCallback(mClientModeCallback); + mActiveModeWarden.registerLohsCallback(mLohsStateMachineCallback); } private ActiveModeWarden createActiveModeWarden() { - return new ActiveModeWarden(mWifiInjector, - mContext, - mLooper.getLooper(), - mWifiNative, - mDefaultModeManager, - mBatteryStats); + ActiveModeWarden warden = new ActiveModeWarden( + mWifiInjector, + mLooper.getLooper(), + mWifiNative, + mDefaultModeManager, + mBatteryStats, + mWifiDiagnostics, + mContext, + mClientModeImpl, + mSettingsStore, + mFacade, + mWifiPermissionsUtil); + // SelfRecovery is created in WifiInjector after ActiveModeWarden, so getSelfRecovery() + // returns null when constructing ActiveModeWarden. + when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery); + return warden; } /** @@ -133,24 +195,55 @@ public class ActiveModeWardenTest { @After public void cleanUp() throws Exception { mActiveModeWarden = null; + mLooper.dispatchAll(); } - private class ClientModeCallback implements ClientModeManager.Listener { - public int currentState = WifiManager.WIFI_STATE_UNKNOWN; + /** + * Helper method to enter the EnabledState and set ClientModeManager in ConnectMode. + */ + private void enterClientModeActiveState() throws Exception { + String fromState = mActiveModeWarden.getCurrentMode(); + when(mClientModeManager.getScanMode()).thenReturn(SCAN_WITH_HIDDEN_NETWORKS); + when(mClientModeManager.isInConnectMode()).thenReturn(true); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + mClientListener.onStarted(); + mLooper.dispatchAll(); - @Override - public void onStateChanged(int state) { - currentState = state; + assertInEnabledState(); + verify(mClientModeManager).start(); + verify(mClientModeManager).switchToConnectMode(); + verify(mScanRequestProxy).enableScanning(true, true); + if (fromState.equals(DISABLED_STATE_STRING)) { + verify(mBatteryStats).noteWifiOn(); } } - private class ScanOnlyModeCallback implements ScanOnlyModeManager.Listener { - public int currentState = WifiManager.WIFI_STATE_UNKNOWN; + /** + * Helper method to enter the EnabledState and set ClientModeManager in ScanOnlyMode. + */ + private void enterScanOnlyModeActiveState() throws Exception { + String fromState = mActiveModeWarden.getCurrentMode(); + when(mClientModeManager.getScanMode()).thenReturn(SCAN_WITHOUT_HIDDEN_NETWORKS); + when(mClientModeManager.isInScanOnlyMode()).thenReturn(true); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + mClientListener.onStarted(); + mLooper.dispatchAll(); - @Override - public void onStateChanged(int state) { - currentState = state; + assertInEnabledState(); + verify(mClientModeManager).start(); + verify(mClientModeManager).switchToScanOnlyMode(); + verify(mScanRequestProxy).enableScanning(true, false); + if (fromState.equals(DISABLED_STATE_STRING)) { + verify(mBatteryStats).noteWifiOn(); } + verify(mBatteryStats).noteWifiState(BatteryStats.WIFI_STATE_OFF_SCANNING, null); } private void enterSoftApActiveMode() throws Exception { @@ -159,101 +252,185 @@ public class ActiveModeWardenTest { } /** - * Helper method to enter the ClientModeActiveState for ActiveModeWarden. + * Helper method to activate SoftApManager. + * + * This method puts the test object into the correct state and verifies steps along the way. */ - private void enterClientModeActiveState() throws Exception { + private void enterSoftApActiveMode(SoftApModeConfiguration softApConfig) throws Exception { String fromState = mActiveModeWarden.getCurrentMode(); - doAnswer( - new Answer<Object>() { - public ClientModeManager answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - mClientListener = (ClientModeManager.Listener) args[0]; - return mClientModeManager; - } - }).when(mWifiInjector).makeClientModeManager( - any(ClientModeManager.Listener.class)); - mActiveModeWarden.enterClientMode(); + mActiveModeWarden.startSoftAp(softApConfig); mLooper.dispatchAll(); - mClientListener.onStateChanged(WifiManager.WIFI_STATE_ENABLED); + mSoftApListener.onStarted(); mLooper.dispatchAll(); - assertEquals(CLIENT_MODE_STATE_STRING, mActiveModeWarden.getCurrentMode()); - verify(mClientModeManager).start(); - if (fromState.equals(SCAN_ONLY_MODE_STATE_STRING)) { - verify(mScanRequestProxy).enableScanning(false, false); + assertInEnabledState(); + assertThat(softApConfig).isEqualTo(mSoftApConfig); + verify(mSoftApManager).start(); + if (fromState.equals(DISABLED_STATE_STRING)) { + verify(mBatteryStats).noteWifiOn(); } - verify(mScanRequestProxy).enableScanning(true, true); - verify(mBatteryStats).noteWifiOn(); } - /** - * Helper method to enter the ScanOnlyModeActiveState for ActiveModeWarden. - */ - private void enterScanOnlyModeActiveState() throws Exception { + private void enterStaDisabledMode(boolean isSoftApModeManagerActive) { String fromState = mActiveModeWarden.getCurrentMode(); - doAnswer( - new Answer<Object>() { - public ScanOnlyModeManager answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - mScanOnlyListener = (ScanOnlyModeManager.Listener) args[0]; - return mScanOnlyModeManager; - } - }).when(mWifiInjector).makeScanOnlyModeManager( - any(ScanOnlyModeManager.Listener.class)); - mActiveModeWarden.enterScanOnlyMode(); - mLooper.dispatchAll(); - mScanOnlyListener.onStateChanged(WifiManager.WIFI_STATE_ENABLED); - mLooper.dispatchAll(); - - assertEquals(SCAN_ONLY_MODE_STATE_STRING, mActiveModeWarden.getCurrentMode()); - verify(mScanOnlyModeManager).start(); - if (fromState.equals(CLIENT_MODE_STATE_STRING)) { + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + if (mClientListener != null) { + mClientListener.onStopped(); + mLooper.dispatchAll(); + } + + if (isSoftApModeManagerActive) { + assertInEnabledState(); + } else { + assertInDisabledState(); + } + if (fromState.equals(ENABLED_STATE_STRING)) { verify(mScanRequestProxy).enableScanning(false, false); } - verify(mScanRequestProxy).enableScanning(true, false); - verify(mBatteryStats).noteWifiOn(); - verify(mBatteryStats).noteWifiState(eq(BatteryStats.WIFI_STATE_OFF_SCANNING), eq(null)); + } + + private void shutdownWifi() { + mActiveModeWarden.recoveryDisableWifi(); + mLooper.dispatchAll(); + } + + private void assertInEnabledState() { + assertThat(mActiveModeWarden.getCurrentMode()).isEqualTo(ENABLED_STATE_STRING); + } + + private void assertInDisabledState() { + assertThat(mActiveModeWarden.getCurrentMode()).isEqualTo(DISABLED_STATE_STRING); } /** - * Helper method to enter the SoftApActiveMode for ActiveModeWarden. + * Emergency mode is a sub-mode within each main state (ScanOnly, Client, DisabledState). + */ + private void assertInEmergencyMode() { + assertThat(mActiveModeWarden.isInEmergencyMode()).isTrue(); + } + + /** + * Counts the number of times a void method was called on a mock. * - * This method puts the test object into the correct state and verifies steps along the way. + * Void methods cannot be passed to Mockito.mockingDetails(). Thus we have to use method name + * matching instead. */ - private void enterSoftApActiveMode(SoftApModeConfiguration softApConfig) throws Exception { - String fromState = mActiveModeWarden.getCurrentMode(); - doAnswer( - new Answer<Object>() { - public SoftApManager answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - mSoftApManagerCallback = (WifiManager.SoftApCallback) args[0]; - assertEquals(softApConfig, (SoftApModeConfiguration) args[1]); - return mSoftApManager; - } - }).when(mWifiInjector).makeSoftApManager(any(WifiManager.SoftApCallback.class), - any()); - mActiveModeWarden.enterSoftAPMode(softApConfig); - mLooper.dispatchAll(); - verify(mSoftApManager).start(); - if (fromState.equals(WIFI_DISABLED_STATE_STRING)) { - verify(mBatteryStats).noteWifiOn(); - } else if (!fromState.equals(SCAN_ONLY_MODE_STATE_STRING) - && !fromState.equals(CLIENT_MODE_STATE_STRING)) { - verify(mScanRequestProxy, atLeastOnce()).enableScanning(false, false); - } + private static int getMethodInvocationCount(Object mock, String methodName) { + long count = mockingDetails(mock).getInvocations() + .stream() + .filter(invocation -> methodName.equals(invocation.getMethod().getName())) + .count(); + return (int) count; + } + + /** + * Counts the number of times a non-void method was called on a mock. + * + * For non-void methods, can pass the method call literal directly: + * e.g. getMethodInvocationCount(mock.method()); + */ + private static int getMethodInvocationCount(Object mockMethod) { + return mockingDetails(mockMethod).getInvocations().size(); + } + + private void assertWifiShutDown(Runnable r) { + assertWifiShutDown(r, 1); } /** - * Test that after starting up, ActiveModeWarden is in the Disabled State. + * Asserts that the runnable r has shut down wifi properly. + * + * @param r runnable that will shut down wifi + * @param times expected number of times that <code>r</code> shut down wifi + */ + private void assertWifiShutDown(Runnable r, int times) { + // take snapshot of ActiveModeManagers + Collection<ActiveModeManager> activeModeManagers = + mActiveModeWarden.getActiveModeManagers(); + + List<Integer> expectedStopInvocationCounts = activeModeManagers + .stream() + .map(manager -> getMethodInvocationCount(manager, "stop") + times) + .collect(Collectors.toList()); + + r.run(); + + List<Integer> actualStopInvocationCounts = activeModeManagers + .stream() + .map(manager -> getMethodInvocationCount(manager, "stop")) + .collect(Collectors.toList()); + + String managerNames = activeModeManagers.stream() + .map(manager -> manager.getClass().getCanonicalName()) + .collect(Collectors.joining(", ", "[", "]")); + + assertThat(actualStopInvocationCounts) + .named(managerNames) + .isEqualTo(expectedStopInvocationCounts); + } + + private void assertEnteredEcmMode(Runnable r) { + assertEnteredEcmMode(r, 1); + } + + /** + * Asserts that the runnable r has entered ECM state properly. + * + * @param r runnable that will enter ECM + * @param times expected number of times that <code>r</code> shut down wifi */ + private void assertEnteredEcmMode(Runnable r, int times) { + // take snapshot of ActiveModeManagers + Collection<ActiveModeManager> activeModeManagers = + mActiveModeWarden.getActiveModeManagers(); + + boolean disableWifiInEcm = mFacade.getConfigWiFiDisableInECBM(mContext); + + List<Integer> expectedStopInvocationCounts = activeModeManagers.stream() + .map(manager -> { + int initialCount = getMethodInvocationCount(manager, "stop"); + // carrier config enabled, all mode managers should have been shut down once + int count = disableWifiInEcm ? initialCount + times : initialCount; + if (manager instanceof SoftApManager) { + // expect SoftApManager.close() to be called + return count + times; + } else { + // don't expect other Managers close() to be called + return count; + } + }) + .collect(Collectors.toList()); + + r.run(); + + assertInEmergencyMode(); + + List<Integer> actualStopInvocationCounts = activeModeManagers.stream() + .map(manager -> getMethodInvocationCount(manager, "stop")) + .collect(Collectors.toList()); + + String managerNames = activeModeManagers.stream() + .map(manager -> manager.getClass().getCanonicalName()) + .collect(Collectors.joining(", ", "[", "]")); + + assertThat(actualStopInvocationCounts) + .named(managerNames) + .isEqualTo(expectedStopInvocationCounts); + } + + /** Test that after starting up, ActiveModeWarden is in the DisabledState State. */ @Test - public void testWifiDisabledAtStartup() throws Exception { - assertEquals(WIFI_DISABLED_STATE_STRING, mActiveModeWarden.getCurrentMode()); + public void testDisabledStateAtStartup() { + assertInDisabledState(); } /** - * Test that ActiveModeWarden properly enters the ScanOnlyModeActiveState from the - * WifiDisabled state. + * Test that ActiveModeWarden properly enters the EnabledState (in ScanOnlyMode) from the + * DisabledState state. */ @Test public void testEnterScanOnlyModeFromDisabled() throws Exception { @@ -261,8 +438,8 @@ public class ActiveModeWardenTest { } /** - * Test that ActiveModeWarden properly enters the SoftApModeActiveState from the - * WifiDisabled state. + * Test that ActiveModeWarden properly starts the SoftApManager from the + * DisabledState state. */ @Test public void testEnterSoftApModeFromDisabled() throws Exception { @@ -270,49 +447,50 @@ public class ActiveModeWardenTest { } /** - * Test that ActiveModeWarden properly enters the SoftApModeActiveState from another state. + * Test that ActiveModeWarden properly starts the SoftApManager from another state. */ @Test public void testEnterSoftApModeFromDifferentState() throws Exception { enterClientModeActiveState(); - mLooper.dispatchAll(); - assertEquals(CLIENT_MODE_STATE_STRING, mActiveModeWarden.getCurrentMode()); + assertInEnabledState(); reset(mBatteryStats, mScanRequestProxy); enterSoftApActiveMode(); } /** - * Test that we can disable wifi fully from the ScanOnlyModeActiveState. + * Test that we can disable wifi fully from the EnabledState (in ScanOnlyMode). */ @Test public void testDisableWifiFromScanOnlyModeActiveState() throws Exception { enterScanOnlyModeActiveState(); - mActiveModeWarden.disableWifi(); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + mActiveModeWarden.scanAlwaysModeChanged(); mLooper.dispatchAll(); - verify(mScanOnlyModeManager).stop(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + + verify(mClientModeManager).stop(); verify(mBatteryStats).noteWifiOff(); - assertEquals(WIFI_DISABLED_STATE_STRING, mActiveModeWarden.getCurrentMode()); + assertInDisabledState(); } /** - * Test that we can disable wifi from the SoftApModeActiveState and not impact softap. + * Test that we can disable wifi when SoftApManager is active and not impact softap. */ @Test public void testDisableWifiFromSoftApModeActiveStateDoesNotStopSoftAp() throws Exception { enterSoftApActiveMode(); + enterScanOnlyModeActiveState(); reset(mDefaultModeManager); - mActiveModeWarden.disableWifi(); - mLooper.dispatchAll(); + enterStaDisabledMode(true); verify(mSoftApManager, never()).stop(); verify(mBatteryStats, never()).noteWifiOff(); - assertEquals(WIFI_DISABLED_STATE_STRING, mActiveModeWarden.getCurrentMode()); } /** - * Thest that we can switch from ScanOnlyActiveMode to another mode. - * Expectation: When switching out of ScanOlyModeActivState we stop the ScanOnlyModeManager. + * Test that we can switch from the EnabledState (in ScanOnlyMode) to another mode. */ @Test public void testSwitchModeWhenScanOnlyModeActiveState() throws Exception { @@ -321,13 +499,25 @@ public class ActiveModeWardenTest { reset(mBatteryStats, mScanRequestProxy); enterClientModeActiveState(); mLooper.dispatchAll(); - verify(mScanOnlyModeManager).stop(); - assertEquals(CLIENT_MODE_STATE_STRING, mActiveModeWarden.getCurrentMode()); + verify(mClientModeManager).switchToConnectMode(); + assertInEnabledState(); + } + + /** + * Reentering EnabledState should be a NOP. + */ + @Test + public void testReenterClientModeActiveStateIsNop() throws Exception { + enterClientModeActiveState(); + reset(mClientModeManager); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + verify(mClientModeManager, never()).start(); } /** - * Test that we can switch from SoftApActiveMode to another mode. - * Expectation: When switching out of SoftApModeActiveState we do not impact softap operation + * Test that we can switch mode when SoftApManager is active to another mode. */ @Test public void testSwitchModeWhenSoftApActiveMode() throws Exception { @@ -338,23 +528,21 @@ public class ActiveModeWardenTest { enterClientModeActiveState(); mLooper.dispatchAll(); verify(mSoftApManager, never()).stop(); - assertEquals(CLIENT_MODE_STATE_STRING, mActiveModeWarden.getCurrentMode()); + assertInEnabledState(); verify(mWifiNative, never()).teardownAllInterfaces(); } /** - * Test that we do enter the SoftApModeActiveState if we are already in WifiDisabledState due to + * Test that we activate SoftApModeManager if we are already in DisabledState due to * a failure. - * Expectations: We should exit the current WifiDisabledState and re-enter before successfully - * entering the SoftApModeActiveState. */ @Test public void testEnterSoftApModeActiveWhenAlreadyInSoftApMode() throws Exception { enterSoftApActiveMode(); // now inject failure through the SoftApManager.Listener - mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); + mSoftApListener.onStartFailure(); mLooper.dispatchAll(); - assertEquals(WIFI_DISABLED_STATE_STRING, mActiveModeWarden.getCurrentMode()); + assertInDisabledState(); // clear the first call to start SoftApManager reset(mSoftApManager, mBatteryStats); @@ -362,63 +550,57 @@ public class ActiveModeWardenTest { } /** - * Test that we return to the WifiDisabledState after a failure is reported when in the - * ScanOnlyModeActiveState. - * Expectations: we should exit the ScanOnlyModeActiveState and stop the ScanOnlyModeManager. + * Test that we return to the DisabledState after a failure is reported when in the + * EnabledState. */ @Test public void testScanOnlyModeFailureWhenActive() throws Exception { enterScanOnlyModeActiveState(); // now inject a failure through the ScanOnlyModeManager.Listener - mScanOnlyListener.onStateChanged(WifiManager.WIFI_STATE_UNKNOWN); + mClientListener.onStartFailure(); mLooper.dispatchAll(); - assertEquals(WIFI_DISABLED_STATE_STRING, mActiveModeWarden.getCurrentMode()); - verify(mScanOnlyModeManager).stop(); + assertInDisabledState(); verify(mBatteryStats).noteWifiOff(); - assertEquals(WifiManager.WIFI_STATE_UNKNOWN, mScanOnlyCallback.currentState); } /** - * Test that we return to the WifiDisabledState after a failure is reported when in the - * SoftApModeActiveState. - * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager. + * Test that we return to the DisabledState after a failure is reported when + * SoftApManager is active. */ @Test public void testSoftApFailureWhenActive() throws Exception { enterSoftApActiveMode(); // now inject failure through the SoftApManager.Listener - mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); + mSoftApListener.onStartFailure(); mLooper.dispatchAll(); verify(mBatteryStats).noteWifiOff(); } /** - * Test that we return to the WifiDisabledState after the ScanOnlyModeManager is stopping in the - * ScanOnlyModeActiveState. - * Expectations: We should exit the ScanOnlyModeActiveState and stop the ScanOnlyModeManager. + * Test that we return to the DisabledState after the ClientModeManager running in ScanOnlyMode + * is stopped. */ @Test public void testScanOnlyModeDisabledWhenActive() throws Exception { enterScanOnlyModeActiveState(); + // now inject the stop message through the ScanOnlyModeManager.Listener - mScanOnlyListener.onStateChanged(WifiManager.WIFI_STATE_DISABLED); + mClientListener.onStopped(); mLooper.dispatchAll(); - assertEquals(WIFI_DISABLED_STATE_STRING, mActiveModeWarden.getCurrentMode()); - verify(mScanOnlyModeManager).stop(); + + assertInDisabledState(); verify(mBatteryStats).noteWifiOff(); } /** - * Test that we return to the WifiDisabledState after the SoftApManager is stopped in the - * SoftApModeActiveState. - * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager. + * Test that we return to the DisabledState after the SoftApManager is stopped. */ @Test public void testSoftApDisabledWhenActive() throws Exception { enterSoftApActiveMode(); reset(mWifiNative); // now inject failure through the SoftApManager.Listener - mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); + mSoftApListener.onStartFailure(); mLooper.dispatchAll(); verify(mBatteryStats).noteWifiOff(); verifyNoMoreInteractions(mWifiNative); @@ -431,6 +613,7 @@ public class ActiveModeWardenTest { public void callsWifiServiceCallbackOnSoftApStateChanged() throws Exception { enterSoftApActiveMode(); + mSoftApListener.onStarted(); mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0); mLooper.dispatchAll(); @@ -446,11 +629,12 @@ public class ActiveModeWardenTest { enterSoftApActiveMode(new SoftApModeConfiguration( WifiManager.IFACE_IP_MODE_LOCAL_ONLY, null)); + mSoftApListener.onStarted(); mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0); mLooper.dispatchAll(); verify(mSoftApStateMachineCallback, never()).onStateChanged(anyInt(), anyInt()); - verify(mSoftApStateMachineCallback, never()).onNumClientsChanged(anyInt()); + verify(mSoftApStateMachineCallback, never()).onConnectedClientsChanged(any()); } /** @@ -474,13 +658,13 @@ public class ActiveModeWardenTest { * Verifies that NumClientsChanged event is being passed from SoftApManager to WifiServiceImpl */ @Test - public void callsWifiServiceCallbackOnSoftApNumClientsChanged() throws Exception { - final int testNumClients = 3; + public void callsWifiServiceCallbackOnSoftApConnectedClientsChanged() throws Exception { + final List<WifiClient> testClients = new ArrayList(); enterSoftApActiveMode(); - mSoftApManagerCallback.onNumClientsChanged(testNumClients); + mSoftApManagerCallback.onConnectedClientsChanged(testClients); mLooper.dispatchAll(); - verify(mSoftApStateMachineCallback).onNumClientsChanged(testNumClients); + verify(mSoftApStateMachineCallback).onConnectedClientsChanged(testClients); } /** @@ -488,53 +672,35 @@ public class ActiveModeWardenTest { * WifiServiceImpl is null. */ @Test - public void testNullCallbackToWifiServiceImplForNumClientsChanged() throws Exception { - - final int testNumClients = 3; + public void testNullCallbackToWifiServiceImplForConnectedClientsChanged() throws Exception { + final List<WifiClient> testClients = new ArrayList(); //set the callback to null mActiveModeWarden.registerSoftApCallback(null); enterSoftApActiveMode(); - mSoftApManagerCallback.onNumClientsChanged(testNumClients); + mSoftApManagerCallback.onConnectedClientsChanged(testClients); - verify(mSoftApStateMachineCallback, never()).onNumClientsChanged(anyInt()); + verify(mSoftApStateMachineCallback, never()).onConnectedClientsChanged(any()); } /** * Test that we remain in the active state when we get a state change update that scan mode is * active. - * Expectations: We should remain in the ScanOnlyModeActive state. */ @Test public void testScanOnlyModeStaysActiveOnEnabledUpdate() throws Exception { enterScanOnlyModeActiveState(); - // now inject failure through the SoftApManager.Listener - mScanOnlyListener.onStateChanged(WifiManager.WIFI_STATE_ENABLED); + // now inject success through the Listener + mClientListener.onStarted(); mLooper.dispatchAll(); - assertEquals(SCAN_ONLY_MODE_STATE_STRING, mActiveModeWarden.getCurrentMode()); - verify(mScanOnlyModeManager, never()).stop(); - } - - /** - * Test that we do not act on unepected state string messages and remain in the active state. - * Expectations: We should remain in the ScanOnlyModeActive state. - */ - @Test - public void testScanOnlyModeStaysActiveOnUnexpectedStateUpdate() throws Exception { - enterScanOnlyModeActiveState(); - // now inject failure through the SoftApManager.Listener - mScanOnlyListener.onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING); - mLooper.dispatchAll(); - assertEquals(SCAN_ONLY_MODE_STATE_STRING, mActiveModeWarden.getCurrentMode()); - verify(mScanOnlyModeManager, never()).stop(); + assertInEnabledState(); + verify(mClientModeManager, never()).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 - * ActiveModeWarden to switch to SoftApMode. */ @Test public void testConfigIsPassedToWifiInjector() throws Exception { @@ -550,8 +716,6 @@ public class ActiveModeWardenTest { * 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 { @@ -575,18 +739,34 @@ public class ActiveModeWardenTest { SoftApModeConfiguration softApConfig2 = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config2); - when(mWifiInjector.makeSoftApManager(any(WifiManager.SoftApCallback.class), - eq(softApConfig1))) - .thenReturn(mSoftApManager); + doAnswer(new Answer<SoftApManager>() { + public SoftApManager answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + mSoftApListener = (ActiveModeManager.Listener) args[0]; + return mSoftApManager; + } + }).when(mWifiInjector).makeSoftApManager(any(ActiveModeManager.Listener.class), + any(WifiManager.SoftApCallback.class), eq(softApConfig1)); // make a second softap manager SoftApManager softapManager = mock(SoftApManager.class); - when(mWifiInjector.makeSoftApManager(any(WifiManager.SoftApCallback.class), - eq(softApConfig2))) - .thenReturn(softapManager); - - mActiveModeWarden.enterSoftAPMode(softApConfig1); - mActiveModeWarden.enterSoftAPMode(softApConfig2); + GeneralUtil.Mutable<ActiveModeManager.Listener> softApListener = + new GeneralUtil.Mutable<>(); + doAnswer(new Answer<SoftApManager>() { + public SoftApManager answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + softApListener.value = (ActiveModeManager.Listener) args[0]; + return softapManager; + } + }).when(mWifiInjector).makeSoftApManager(any(ActiveModeManager.Listener.class), + any(WifiManager.SoftApCallback.class), eq(softApConfig2)); + + mActiveModeWarden.startSoftAp(softApConfig1); mLooper.dispatchAll(); + mSoftApListener.onStarted(); + mActiveModeWarden.startSoftAp(softApConfig2); + mLooper.dispatchAll(); + softApListener.value.onStarted(); + verify(mSoftApManager).start(); verify(softapManager).start(); verify(mBatteryStats).noteWifiOn(); @@ -594,12 +774,11 @@ public class ActiveModeWardenTest { /** * Test that we safely disable wifi if it is already disabled. - * Expectations: We should not interact with WifiNative since we should have already cleaned up - * everything. */ @Test public void disableWifiWhenAlreadyOff() throws Exception { - mActiveModeWarden.disableWifi(); + enterStaDisabledMode(false); + verifyZeroInteractions(mWifiNative); } /** @@ -633,12 +812,12 @@ public class ActiveModeWardenTest { public void shutdownWifiDoesNotCrashWhenClientModeExitsOnDestroyed() throws Exception { enterClientModeActiveState(); - mClientListener.onStateChanged(WifiManager.WIFI_STATE_DISABLED); + mClientListener.onStopped(); mLooper.dispatchAll(); - mActiveModeWarden.shutdownWifi(); + shutdownWifi(); - assertEquals(WifiManager.WIFI_STATE_DISABLED, mClientModeCallback.currentState); + assertInDisabledState(); } /** @@ -648,12 +827,12 @@ public class ActiveModeWardenTest { public void onDestroyedCallbackDoesNotCrashWhenClientModeAlreadyStopped() throws Exception { enterClientModeActiveState(); - mActiveModeWarden.shutdownWifi(); + shutdownWifi(); - mClientListener.onStateChanged(WifiManager.WIFI_STATE_DISABLED); + mClientListener.onStopped(); mLooper.dispatchAll(); - assertEquals(WifiManager.WIFI_STATE_DISABLED, mClientModeCallback.currentState); + assertInDisabledState(); } /** @@ -663,10 +842,12 @@ public class ActiveModeWardenTest { public void shutdownWifiDoesNotCrashWhenSoftApExitsOnDestroyed() throws Exception { enterSoftApActiveMode(); + mSoftApListener.onStopped(); + mLooper.dispatchAll(); mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); mLooper.dispatchAll(); - mActiveModeWarden.shutdownWifi(); + shutdownWifi(); verify(mSoftApStateMachineCallback).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); } @@ -678,8 +859,9 @@ public class ActiveModeWardenTest { public void onDestroyedCallbackDoesNotCrashWhenSoftApModeAlreadyStopped() throws Exception { enterSoftApActiveMode(); - mActiveModeWarden.shutdownWifi(); + shutdownWifi(); + mSoftApListener.onStopped(); mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); mLooper.dispatchAll(); @@ -703,17 +885,13 @@ public class ActiveModeWardenTest { public void dumpCallsActiveModeManagers() throws Exception { enterSoftApActiveMode(); enterClientModeActiveState(); - reset(mScanRequestProxy); - enterScanOnlyModeActiveState(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); PrintWriter writer = new PrintWriter(stream); mActiveModeWarden.dump(null, writer, null); - verify(mSoftApManager).dump(eq(null), eq(writer), eq(null)); - // can only be in scan or client, so we should not have a client mode active - verify(mClientModeManager, never()).dump(eq(null), eq(writer), eq(null)); - verify(mScanOnlyModeManager).dump(eq(null), eq(writer), eq(null)); + verify(mSoftApManager).dump(null, writer, null); + verify(mClientModeManager).dump(null, writer, null); } /** @@ -732,27 +910,1134 @@ public class ActiveModeWardenTest { // mock SoftAPManagers when(mSoftApManager.getIpMode()).thenReturn(WifiManager.IFACE_IP_MODE_TETHERED); - when(mWifiInjector.makeSoftApManager(any(WifiManager.SoftApCallback.class), - eq(tetherConfig))) - .thenReturn(mSoftApManager); + doAnswer(new Answer<SoftApManager>() { + public SoftApManager answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + mSoftApListener = (ActiveModeManager.Listener) args[0]; + return mSoftApManager; + } + }).when(mWifiInjector).makeSoftApManager(any(ActiveModeManager.Listener.class), + any(WifiManager.SoftApCallback.class), eq(tetherConfig)); + // make a second softap manager SoftApManager lohsSoftapManager = mock(SoftApManager.class); - when(lohsSoftapManager.getIpMode()).thenReturn(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - when(mWifiInjector.makeSoftApManager(any(WifiManager.SoftApCallback.class), - eq(lohsConfig))) - .thenReturn(lohsSoftapManager); + GeneralUtil.Mutable<ActiveModeManager.Listener> lohsSoftApListener = + new GeneralUtil.Mutable<>(); + doAnswer(new Answer<SoftApManager>() { + public SoftApManager answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + lohsSoftApListener.value = (ActiveModeManager.Listener) args[0]; + return lohsSoftapManager; + } + }).when(mWifiInjector).makeSoftApManager(any(ActiveModeManager.Listener.class), + any(WifiManager.SoftApCallback.class), eq(lohsConfig)); // enable tethering and LOHS - mActiveModeWarden.enterSoftAPMode(tetherConfig); - mActiveModeWarden.enterSoftAPMode(lohsConfig); + mActiveModeWarden.startSoftAp(tetherConfig); mLooper.dispatchAll(); + mSoftApListener.onStarted(); + mActiveModeWarden.startSoftAp(lohsConfig); + mLooper.dispatchAll(); + lohsSoftApListener.value.onStarted(); verify(mSoftApManager).start(); verify(lohsSoftapManager).start(); verify(mBatteryStats).noteWifiOn(); // disable tethering - mActiveModeWarden.stopSoftAPMode(WifiManager.IFACE_IP_MODE_TETHERED); + mActiveModeWarden.stopSoftAp(WifiManager.IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); verify(mSoftApManager).stop(); verify(lohsSoftapManager, never()).stop(); } + + /** + * Verify that toggling wifi from disabled starts client mode. + */ + @Test + public void enableWifi() throws Exception { + assertInDisabledState(); + + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + + mClientListener.onStarted(); + mLooper.dispatchAll(); + + assertInEnabledState(); + } + + /** + * Test verifying that we can enter scan mode when the scan mode changes + */ + @Test + public void enableScanMode() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + mActiveModeWarden.scanAlwaysModeChanged(); + mLooper.dispatchAll(); + verify(mClientModeManager).start(); + verify(mClientModeManager).switchToScanOnlyMode(); + assertInEnabledState(); + verify(mClientModeManager, never()).stop(); + } + + /** + * Verify that if scanning is enabled at startup, we enter scan mode + */ + @Test + public void testEnterScanModeAtStartWhenSet() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + + mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); + mLooper.dispatchAll(); + + assertInEnabledState(); + } + + /** + * Verify that if Wifi is enabled at startup, we enter client mode + */ + @Test + public void testEnterClientModeAtStartWhenSet() throws Exception { + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + + mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); + mLooper.dispatchAll(); + + assertInEnabledState(); + + verify(mClientModeManager).start(); + verify(mClientModeManager).switchToConnectMode(); + } + + /** + * Do not enter scan mode if location mode disabled. + */ + @Test + public void testDoesNotEnterScanModeWhenLocationModeDisabled() throws Exception { + // Start a new WifiController with wifi disabled + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); + + mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); + mLooper.dispatchAll(); + + assertInDisabledState(); + + // toggling scan always available is not sufficient for scan mode + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + mActiveModeWarden.scanAlwaysModeChanged(); + mLooper.dispatchAll(); + + assertInDisabledState(); + } + + /** + * Only enter scan mode if location mode enabled + */ + @Test + public void testEnterScanModeWhenLocationModeEnabled() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); + + reset(mContext); + when(mContext.getResources()).thenReturn(mResources); + mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); + mLooper.dispatchAll(); + + ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(bcastRxCaptor.capture(), any(IntentFilter.class)); + BroadcastReceiver broadcastReceiver = bcastRxCaptor.getValue(); + + assertInDisabledState(); + + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION); + broadcastReceiver.onReceive(mContext, intent); + mLooper.dispatchAll(); + + assertInEnabledState(); + } + + + /** + * Disabling location mode when in scan mode will disable wifi + */ + @Test + public void testExitScanModeWhenLocationModeDisabled() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + + reset(mContext); + when(mContext.getResources()).thenReturn(mResources); + mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); + mLooper.dispatchAll(); + mClientListener.onStarted(); + mLooper.dispatchAll(); + + ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(bcastRxCaptor.capture(), any(IntentFilter.class)); + BroadcastReceiver broadcastReceiver = bcastRxCaptor.getValue(); + + assertInEnabledState(); + + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); + Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION); + broadcastReceiver.onReceive(mContext, intent); + mLooper.dispatchAll(); + + mClientListener.onStopped(); + mLooper.dispatchAll(); + + assertInDisabledState(); + } + + /** + * When in Client mode, make sure ECM triggers wifi shutdown. + */ + @Test + public void testEcmOnFromClientMode() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + enableWifi(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertWifiShutDown(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + } + + /** + * ECM disabling messages, when in client mode (not expected) do not trigger state changes. + */ + @Test + public void testEcmOffInClientMode() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + enableWifi(); + + // Test with WifiDisableInECBM turned off + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false); + + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + } + + /** + * When ECM activates and we are in client mode, disabling ECM should return us to client mode. + */ + @Test + public void testEcmDisabledReturnsToClientMode() throws Exception { + enableWifi(); + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertWifiShutDown(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + + assertInEnabledState(); + } + + /** + * When Ecm mode is enabled, we should shut down wifi when we get an emergency mode changed + * update. + */ + @Test + public void testEcmOnFromScanMode() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + mActiveModeWarden.scanAlwaysModeChanged(); + mLooper.dispatchAll(); + + mClientListener.onStarted(); + mLooper.dispatchAll(); + + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertWifiShutDown(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + } + + /** + * When Ecm mode is disabled, we should not shut down scan mode if we get an emergency mode + * changed update, but we should turn off soft AP + */ + @Test + public void testEcmOffInScanMode() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + mActiveModeWarden.scanAlwaysModeChanged(); + mLooper.dispatchAll(); + + assertInEnabledState(); + + // Test with WifiDisableInECBM turned off: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false); + + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + } + + /** + * When ECM is disabled, we should return to scan mode + */ + @Test + public void testEcmDisabledReturnsToScanMode() throws Exception { + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + mActiveModeWarden.scanAlwaysModeChanged(); + mLooper.dispatchAll(); + + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertWifiShutDown(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + + assertInEnabledState(); + } + + /** + * When Ecm mode is enabled, we should shut down wifi when we get an emergency mode changed + * update. + */ + @Test + public void testEcmOnFromSoftApMode() throws Exception { + enterSoftApActiveMode(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + } + + /** + * When Ecm mode is disabled, we should shut down softap mode if we get an emergency mode + * changed update + */ + @Test + public void testEcmOffInSoftApMode() throws Exception { + enterSoftApActiveMode(); + + // Test with WifiDisableInECBM turned off: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false); + + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + + verify(mSoftApManager).stop(); + } + + /** + * When ECM is activated and we were in softap mode, we should just return to wifi off when ECM + * ends + */ + @Test + public void testEcmDisabledRemainsDisabledWhenSoftApHadBeenOn() throws Exception { + assertInDisabledState(); + + enterSoftApActiveMode(); + + // verify Soft AP Manager started + verify(mSoftApManager).start(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + mSoftApListener.onStopped(); + mLooper.dispatchAll(); + }); + + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + + assertInDisabledState(); + + // verify no additional calls to enable softap + verify(mSoftApManager).start(); + } + + /** + * Wifi should remain off when already disabled and we enter ECM. + */ + @Test + public void testEcmOnFromDisabledMode() throws Exception { + assertInDisabledState(); + verify(mSoftApManager, never()).start(); + verify(mClientModeManager, never()).start(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + } + + + /** + * Updates about call state change also trigger entry of ECM mode. + */ + @Test + public void testEnterEcmOnEmergencyCallStateChange() throws Exception { + assertInDisabledState(); + + enableWifi(); + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + // test call state changed + mActiveModeWarden.emergencyCallStateChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + + mActiveModeWarden.emergencyCallStateChanged(false); + mLooper.dispatchAll(); + + assertInEnabledState(); + } + + /** + * Verify when both ECM and call state changes arrive, we enter ECM mode + */ + @Test + public void testEnterEcmWithBothSignals() throws Exception { + assertInDisabledState(); + + enableWifi(); + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertWifiShutDown(() -> { + mActiveModeWarden.emergencyCallStateChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + + assertWifiShutDown(() -> { + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }, 0); // does not cause another shutdown + + // client mode only started once so far + verify(mClientModeManager).start(); + + mActiveModeWarden.emergencyCallStateChanged(false); + mLooper.dispatchAll(); + + // stay in ecm, do not send an additional client mode trigger + assertInEmergencyMode(); + // assert that the underlying state is in disabled state + assertInDisabledState(); + + // client mode still only started once + verify(mClientModeManager).start(); + + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + + // now we can re-enable wifi + verify(mClientModeManager, times(2)).start(); + assertInEnabledState(); + } + + /** + * Verify when both ECM and call state changes arrive but out of order, we enter ECM mode + */ + @Test + public void testEnterEcmWithBothSignalsOutOfOrder() throws Exception { + assertInDisabledState(); + + enableWifi(); + + assertInEnabledState(); + verify(mClientModeManager).start(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + assertInDisabledState(); + + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallStateChanged(true); + mLooper.dispatchAll(); + }, 0); // does not enter ECM state again + + mActiveModeWarden.emergencyCallStateChanged(false); + mLooper.dispatchAll(); + + // stay in ecm, do not send an additional client mode trigger + assertInEmergencyMode(); + // assert that the underlying state is in disabled state + assertInDisabledState(); + + // client mode still only started once + verify(mClientModeManager).start(); + + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + + // now we can re-enable wifi + verify(mClientModeManager, times(2)).start(); + assertInEnabledState(); + } + + /** + * Verify when both ECM and call state changes arrive but completely out of order, + * we still enter and properly exit ECM mode + */ + @Test + public void testEnterEcmWithBothSignalsOppositeOrder() throws Exception { + assertInDisabledState(); + + enableWifi(); + + assertInEnabledState(); + verify(mClientModeManager).start(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallStateChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + assertInDisabledState(); + + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }, 0); // still only 1 shutdown + + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + + // stay in ecm, do not send an additional client mode trigger + assertInEmergencyMode(); + // assert that the underlying state is in disabled state + assertInDisabledState(); + + // client mode still only started once + verify(mClientModeManager).start(); + + mActiveModeWarden.emergencyCallStateChanged(false); + mLooper.dispatchAll(); + + // now we can re-enable wifi + verify(mClientModeManager, times(2)).start(); + assertInEnabledState(); + } + + /** + * When ECM is active, we might get addition signals of ECM mode, drop those additional signals, + * we must exit when one of each signal is received. + * + * In any case, duplicate signals indicate a bug from Telephony. Each signal should be turned + * off before it is turned on again. + */ + @Test + public void testProperExitFromEcmModeWithMultipleMessages() throws Exception { + assertInDisabledState(); + + enableWifi(); + + verify(mClientModeManager).start(); + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallbackModeChanged(true); + mActiveModeWarden.emergencyCallStateChanged(true); + mActiveModeWarden.emergencyCallStateChanged(true); + mActiveModeWarden.emergencyCallbackModeChanged(true); + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + assertInDisabledState(); + + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + }, 0); + + // didn't enter client mode again + verify(mClientModeManager).start(); + assertInDisabledState(); + + // now we will exit ECM + mActiveModeWarden.emergencyCallStateChanged(false); + mLooper.dispatchAll(); + + // now we can re-enable wifi + verify(mClientModeManager, times(2)).start(); + assertInEnabledState(); + } + + /** + * Toggling wifi when in ECM does not exit ecm mode and enable wifi + */ + @Test + public void testWifiDoesNotToggleOnWhenInEcm() throws Exception { + assertInDisabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + // test ecm changed + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + + // now toggle wifi and verify we do not start wifi + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + + verify(mClientModeManager, never()).start(); + assertInDisabledState(); + } + + @Test + public void testAirplaneModeDoesNotToggleOnWhenInEcm() throws Exception { + // TODO(b/139829963): investigate the expected behavior is when toggling airplane mode in + // ECM + } + + /** + * Toggling scan mode when in ECM does not exit ecm mode and enable scan mode + */ + @Test + public void testScanModeDoesNotToggleOnWhenInEcm() throws Exception { + assertInDisabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + + // now enable scanning and verify we do not start wifi + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + mActiveModeWarden.scanAlwaysModeChanged(); + mLooper.dispatchAll(); + + verify(mClientModeManager, never()).start(); + assertInDisabledState(); + } + + + /** + * Toggling softap mode when in ECM does not exit ecm mode and enable softap + */ + @Test + public void testSoftApModeDoesNotToggleOnWhenInEcm() throws Exception { + assertInDisabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + + mActiveModeWarden.startSoftAp( + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null)); + mLooper.dispatchAll(); + + verify(mSoftApManager, never()).start(); + assertInDisabledState(); + } + + /** + * Toggling off softap mode when in ECM does not induce a mode change + */ + @Test + public void testSoftApStoppedDoesNotSwitchModesWhenInEcm() throws Exception { + assertInDisabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + + mActiveModeWarden.stopSoftAp(WifiManager.IFACE_IP_MODE_UNSPECIFIED); + mLooper.dispatchAll(); + + assertInDisabledState(); + verifyNoMoreInteractions(mSoftApManager, mClientModeManager); + } + + /** + * Toggling softap mode when in airplane mode needs to enable softap + */ + @Test + public void testSoftApModeToggleWhenInAirplaneMode() throws Exception { + // Test with airplane mode turned on: + when(mSettingsStore.isAirplaneModeOn()).thenReturn(true); + + // Turn on SoftAp. + mActiveModeWarden.startSoftAp( + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null)); + mLooper.dispatchAll(); + verify(mSoftApManager).start(); + + // Turn off SoftAp. + mActiveModeWarden.stopSoftAp(WifiManager.IFACE_IP_MODE_UNSPECIFIED); + mLooper.dispatchAll(); + + verify(mSoftApManager).stop(); + } + + /** + * Toggling off scan mode when in ECM does not induce a mode change + */ + @Test + public void testScanModeStoppedSwitchModeToDisabledStateWhenInEcm() throws Exception { + enterScanOnlyModeActiveState(); + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + + // Spurious onStopped + mClientListener.onStopped(); + mLooper.dispatchAll(); + + assertInDisabledState(); + } + + /** + * Toggling off client mode when in ECM does not induce a mode change + */ + @Test + public void testClientModeStoppedSwitchModeToDisabledStateWhenInEcm() throws Exception { + enterClientModeActiveState(); + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + assertEnteredEcmMode(() -> { + // test ecm changed + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + + // Spurious onStopped + mClientListener.onStopped(); + mLooper.dispatchAll(); + + assertInDisabledState(); + } + + /** + * When AP mode is enabled and wifi was previously in AP mode, we should return to + * EnabledState after the AP is disabled. + * Enter EnabledState, activate AP mode, disable AP mode. + * <p> + * Expected: AP should successfully start and exit, then return to EnabledState. + */ + @Test + public void testReturnToEnabledStateAfterAPModeShutdown() throws Exception { + enableWifi(); + assertInEnabledState(); + verify(mClientModeManager).start(); + + mActiveModeWarden.startSoftAp( + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null)); + // add an "unexpected" sta mode stop to simulate a single interface device + mClientListener.onStopped(); + mLooper.dispatchAll(); + + // Now stop the AP + mSoftApListener.onStopped(); + mLooper.dispatchAll(); + + // We should re-enable client mode + verify(mClientModeManager, times(2)).start(); + assertInEnabledState(); + } + + /** + * When in STA mode and SoftAP is enabled and the device supports STA+AP (i.e. the STA wasn't + * shut down when the AP started), both modes will be running concurrently. + * + * Then when the AP is disabled, we should remain in STA mode. + * + * Enter EnabledState, activate AP mode, toggle WiFi off. + * <p> + * Expected: AP should successfully start and exit, then return to EnabledState. + */ + @Test + public void testReturnToEnabledStateAfterWifiEnabledShutdown() throws Exception { + enableWifi(); + assertInEnabledState(); + verify(mClientModeManager).start(); + + mActiveModeWarden.startSoftAp( + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null)); + mLooper.dispatchAll(); + + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + mActiveModeWarden.wifiToggled(); + mSoftApListener.onStopped(); + mLooper.dispatchAll(); + + // wasn't called again + verify(mClientModeManager).start(); + assertInEnabledState(); + } + + @Test + public void testRestartWifiStackInEnabledStateTriggersBugReport() throws Exception { + enableWifi(); + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + mLooper.dispatchAll(); + verify(mClientModeImpl).takeBugReport(anyString(), anyString()); + } + + @Test + public void testRestartWifiWatchdogDoesNotTriggerBugReport() throws Exception { + enableWifi(); + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + mLooper.dispatchAll(); + verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString()); + } + + /** + * When in sta mode, CMD_RECOVERY_DISABLE_WIFI messages should trigger wifi to disable. + */ + @Test + public void testRecoveryDisabledTurnsWifiOff() throws Exception { + enableWifi(); + assertInEnabledState(); + mActiveModeWarden.recoveryDisableWifi(); + mLooper.dispatchAll(); + verify(mClientModeManager).stop(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + assertInDisabledState(); + } + + /** + * When wifi is disabled, CMD_RECOVERY_DISABLE_WIFI should not trigger a state change. + */ + @Test + public void testRecoveryDisabledWhenWifiAlreadyOff() throws Exception { + assertInDisabledState(); + assertWifiShutDown(() -> { + mActiveModeWarden.recoveryDisableWifi(); + mLooper.dispatchAll(); + }); + } + + /** + * The command to trigger a WiFi reset should not trigger any action by WifiController if we + * are not in STA mode. + * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures + * should be ignored. + * Create and start WifiController in DisabledState, send command to restart WiFi + * <p> + * Expected: WiFiController should not call ActiveModeWarden.disableWifi() + */ + @Test + public void testRestartWifiStackInDisabledState() throws Exception { + assertInDisabledState(); + + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + mLooper.dispatchAll(); + + assertInDisabledState(); + verifyNoMoreInteractions(mClientModeManager, mSoftApManager); + } + + /** + * The command to trigger a WiFi reset should trigger a wifi reset in ClientModeImpl through + * the ActiveModeWarden.shutdownWifi() call when in STA mode. + * When WiFi is in scan mode, calls to reset the wifi stack due to native failure + * should trigger a supplicant stop, and subsequently, a driver reload. + * Create and start WifiController in EnabledState, send command to restart WiFi + * <p> + * Expected: WiFiController should call ActiveModeWarden.shutdownWifi() and + * ActiveModeWarden should enter SCAN_ONLY mode and the wifi driver should be started. + */ + @Test + public void testRestartWifiStackInDisabledStateWithScanState() throws Exception { + assertInDisabledState(); + + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + mActiveModeWarden.scanAlwaysModeChanged(); + mLooper.dispatchAll(); + + assertInEnabledState(); + verify(mClientModeManager).start(); + verify(mClientModeManager).switchToScanOnlyMode(); + + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + mLooper.dispatchAll(); + + verify(mClientModeManager).stop(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + assertInDisabledState(); + + mLooper.moveTimeForward(TEST_WIFI_RECOVERY_DELAY_MS); + mLooper.dispatchAll(); + + verify(mClientModeManager, times(2)).start(); + verify(mClientModeManager, times(2)).switchToScanOnlyMode(); + assertInEnabledState(); + } + + /** + * The command to trigger a WiFi reset should trigger a wifi reset in ClientModeImpl through + * the ActiveModeWarden.shutdownWifi() call when in STA mode. + * WiFi is in connect mode, calls to reset the wifi stack due to connection failures + * should trigger a supplicant stop, and subsequently, a driver reload. + * Create and start WifiController in EnabledState, send command to restart WiFi + * <p> + * Expected: WiFiController should call ActiveModeWarden.shutdownWifi() and + * ActiveModeWarden should enter CONNECT_MODE and the wifi driver should be started. + */ + @Test + public void testRestartWifiStackInEnabledState() throws Exception { + enableWifi(); + assertInEnabledState(); + verify(mClientModeManager).start(); + + assertWifiShutDown(() -> { + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + mLooper.dispatchAll(); + // Complete the stop + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + + // still only started once + verify(mClientModeManager).start(); + + mLooper.moveTimeForward(TEST_WIFI_RECOVERY_DELAY_MS); + mLooper.dispatchAll(); + + // started again + verify(mClientModeManager, times(2)).start(); + assertInEnabledState(); + } + + /** + * The command to trigger a WiFi reset should not trigger a reset when in ECM mode. + * Enable wifi and enter ECM state, send command to restart wifi. + * <p> + * Expected: The command to trigger a wifi reset should be ignored and we should remain in ECM + * mode. + */ + @Test + public void testRestartWifiStackDoesNotExitECMMode() throws Exception { + enableWifi(); + assertInEnabledState(); + verify(mClientModeManager).start(); + verify(mClientModeManager).getScanMode(); + verify(mClientModeManager).isInScanOnlyMode(); + verify(mClientModeManager).switchToConnectMode(); + + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallStateChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(); + mLooper.dispatchAll(); + }); + assertInEmergencyMode(); + assertInDisabledState(); + verify(mClientModeManager).stop(); + + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + mLooper.dispatchAll(); + + verify(mClientModeManager).start(); // wasn't called again + assertInEmergencyMode(); + assertInDisabledState(); + verifyNoMoreInteractions(mClientModeManager, mSoftApManager); + } + + /** + * The command to trigger a WiFi reset should trigger a reset when in AP mode. + * Enter AP mode, send command to restart wifi. + * <p> + * Expected: The command to trigger a wifi reset should trigger wifi shutdown. + */ + @Test + public void testRestartWifiStackFullyStopsWifi() throws Exception { + mActiveModeWarden.startSoftAp(new SoftApModeConfiguration( + WifiManager.IFACE_IP_MODE_LOCAL_ONLY, null)); + mLooper.dispatchAll(); + verify(mSoftApManager).start(); + + assertWifiShutDown(() -> { + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_STA_IFACE_DOWN); + mLooper.dispatchAll(); + }); + } + + /** + * Tests that when Wifi is already disabled and another Wifi toggle command arrives, + * don't enter scan mode if {@link WifiSettingsStore#isScanAlwaysAvailable()} is false. + * Note: {@link WifiSettingsStore#isScanAlwaysAvailable()} returns false if either the wifi + * scanning is disabled and airplane mode is on. + */ + @Test + public void staDisabled_toggleWifiOff_scanNotAvailable_dontGoToScanMode() { + assertInDisabledState(); + + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + when(mSettingsStore.isAirplaneModeOn()).thenReturn(true); + + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + + assertInDisabledState(); + verify(mClientModeManager, never()).start(); + } + + /** + * Tests that when Wifi is already disabled and another Wifi toggle command arrives, + * enter scan mode if {@link WifiSettingsStore#isScanAlwaysAvailable()} is true. + * Note: {@link WifiSettingsStore#isScanAlwaysAvailable()} returns true if both the wifi + * scanning is enabled and airplane mode is off. + */ + @Test + public void staDisabled_toggleWifiOff_scanAvailable_goToScanMode() { + assertInDisabledState(); + + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); + + mActiveModeWarden.wifiToggled(); + mLooper.dispatchAll(); + + assertInEnabledState(); + verify(mClientModeManager).start(); + verify(mClientModeManager).switchToScanOnlyMode(); + } + + /** + * Tests that if the carrier config to disable Wifi is enabled during ECM, Wifi is shut down + * when entering ECM and turned back on when exiting ECM. + */ + @Test + public void ecmDisablesWifi_exitEcm_restartWifi() throws Exception { + enterClientModeActiveState(); + + verify(mClientModeManager).start(); + + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + assertEnteredEcmMode(() -> { + mActiveModeWarden.emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + assertInEnabledState(); + verify(mClientModeManager).stop(); + + mActiveModeWarden.emergencyCallbackModeChanged(false); + mLooper.dispatchAll(); + + assertThat(mActiveModeWarden.isInEmergencyMode()).isFalse(); + // client mode restarted + verify(mClientModeManager, times(2)).start(); + assertInEnabledState(); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/BinderUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/BinderUtilTest.java index 74ad17f538..af570a1e6c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/BinderUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/BinderUtilTest.java @@ -31,7 +31,7 @@ import org.junit.Test; * Unit tests for {@link com.android.server.wifi.BinderUtil}. */ @SmallTest -public class BinderUtilTest { +public class BinderUtilTest extends WifiBaseTest { static final int FAKE_UID = 30000000; private long mToken; diff --git a/service/tests/wifitests/src/com/android/server/wifi/ByteBufferReaderTest.java b/service/tests/wifitests/src/com/android/server/wifi/ByteBufferReaderTest.java index add8f14e03..c2a4021dd5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ByteBufferReaderTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ByteBufferReaderTest.java @@ -32,7 +32,7 @@ import java.nio.charset.StandardCharsets; * Unit tests for {@link com.android.server.wifi.ByteBufferReader}. */ @SmallTest -public class ByteBufferReaderTest { +public class ByteBufferReaderTest extends WifiBaseTest { /** * Verify that BufferUnderflowException will be thrown when reading an integer from a buffer * that contained less data than needed. diff --git a/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java b/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java index 3db50c5f28..037fd14ab4 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java @@ -45,7 +45,7 @@ import java.util.List; */ @SmallTest @RunWith(Parameterized.class) -public class CandidateScorerTest { +public class CandidateScorerTest extends WifiBaseTest { @Parameters(name = "{index}: {0}") public static List<Object[]> listOfObjectArraysBecauseJUnitMadeUs() { @@ -166,8 +166,8 @@ public class CandidateScorerTest { */ @Test public void testPreferTheCurrentNetworkEvenIfRssiDifferenceIsSignificant() throws Exception { - assertThat(evaluate(mCandidate1.setScanRssi(-77).setCurrentNetwork(true)), - greaterThan(evaluate(mCandidate2.setScanRssi(-68)))); + assertThat(evaluate(mCandidate1.setScanRssi(-74).setCurrentNetwork(true)), + greaterThan(evaluate(mCandidate2.setScanRssi(-65)))); } /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java b/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java index d89358df03..45b340f363 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java @@ -27,6 +27,7 @@ import android.database.ContentObserver; import android.net.Uri; import android.net.wifi.EAPConstants; import android.net.wifi.WifiEnterpriseConfig; +import android.os.Handler; import android.os.PersistableBundle; import android.os.test.TestLooper; import android.telephony.CarrierConfigManager; @@ -52,7 +53,7 @@ import java.util.Collections; * Unit tests for {@link com.android.server.wifi.CarrierNetworkConfig}. */ @SmallTest -public class CarrierNetworkConfigTest { +public class CarrierNetworkConfigTest extends WifiBaseTest { private static final String TEST_SSID = "Test SSID"; private static final int TEST_STANDARD_EAP_TYPE = EAPConstants.EAP_SIM; private static final int TEST_INTERNAL_EAP_TYPE = WifiEnterpriseConfig.Eap.SIM; @@ -108,11 +109,11 @@ public class CarrierNetworkConfigTest { when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID)) .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE)); when(mSubscriptionManager.getActiveSubscriptionInfoList()) - .thenReturn(Arrays.asList(new SubscriptionInfo[] {TEST_SUBSCRIPTION_INFO})); + .thenReturn(Arrays.asList(TEST_SUBSCRIPTION_INFO)); when(mDataTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN)) .thenReturn(mImsiEncryptionInfo); mLooper = new TestLooper(); - mCarrierNetworkConfig = new CarrierNetworkConfig(mContext, mLooper.getLooper(), + mCarrierNetworkConfig = new CarrierNetworkConfig(mContext, new Handler(mLooper.getLooper()), mFrameworkFacade); ArgumentCaptor<BroadcastReceiver> receiver = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -153,7 +154,7 @@ public class CarrierNetworkConfigTest { null, 0, null, "0", "0", null, false, null, null); when(mSubscriptionManager.getActiveSubscriptionInfoList()) .thenReturn(Collections.singletonList(testSubscriptionInfoNullDisplayName)); - mCarrierNetworkConfig = new CarrierNetworkConfig(mContext, mLooper.getLooper(), + mCarrierNetworkConfig = new CarrierNetworkConfig(mContext, new Handler(mLooper.getLooper()), mFrameworkFacade); reset(mCarrierConfigManager); diff --git a/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java index c4d5864bf3..01b14a6128 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java @@ -58,7 +58,7 @@ import java.util.Map; * Unit tests for CarrierNeteworkEvaluator */ @SmallTest -public class CarrierNetworkEvaluatorTest { +public class CarrierNetworkEvaluatorTest extends WifiBaseTest { private static final String CARRIER1_SSID = "\"carrier1\""; private static final String CARRIER2_SSID = "\"carrier2\""; private static final String CARRIER_SAVED_SSID = "\"carrier3-saved\""; @@ -142,7 +142,8 @@ public class CarrierNetworkEvaluatorTest { mGetConfiguredNetworkForScanDetailsAnswer.addConfig(scanDetail, newConfig); } - when(mWifiConfigManager.enableNetwork(networkId, false, Process.WIFI_UID)).thenReturn(true); + when(mWifiConfigManager.enableNetwork( + networkId, false, Process.WIFI_UID, null)).thenReturn(true); when(mWifiConfigManager.setNetworkCandidateScanResult(eq(networkId), any(), anyInt())).thenReturn(true); when(mWifiConfigManager.getConfiguredNetwork(networkId)).thenReturn(newConfig); diff --git a/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkNotifierTest.java b/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkNotifierTest.java deleted file mode 100644 index 91a9a5de71..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/CarrierNetworkNotifierTest.java +++ /dev/null @@ -1,819 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.CarrierNetworkNotifier.DEFAULT_REPEAT_DELAY_SEC; -import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK; -import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK; -import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE; -import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_USER_DISMISSED_NOTIFICATION; -import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.AVAILABLE_NETWORK_NOTIFIER_TAG; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.NotificationManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.net.Uri; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiManager; -import android.os.Message; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.test.TestLooper; -import android.provider.Settings; - -import androidx.test.filters.SmallTest; - -import com.android.server.wifi.nano.WifiMetricsProto; -import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; - -/** - * Unit tests for {@link CarrierNetworkNotifier}. - */ -@SmallTest -public class CarrierNetworkNotifierTest { - - private static final String TEST_SSID_1 = "Test SSID 1"; - private static final String TEST_SSID_2 = "Test SSID 2"; - private static final int MIN_RSSI_LEVEL = -127; - private static final String CARRIER_NET_NOTIFIER_TAG = CarrierNetworkNotifier.TAG; - private static final int TEST_NETWORK_ID = 42; - - @Mock private Context mContext; - @Mock private Resources mResources; - @Mock private FrameworkFacade mFrameworkFacade; - @Mock private WifiMetrics mWifiMetrics; - @Mock private Clock mClock; - @Mock private WifiConfigStore mWifiConfigStore; - @Mock private WifiConfigManager mWifiConfigManager; - @Mock private NotificationManager mNotificationManager; - @Mock private ClientModeImpl mClientModeImpl; - @Mock private ConnectToNetworkNotificationBuilder mNotificationBuilder; - @Mock private UserManager mUserManager; - private CarrierNetworkNotifier mNotificationController; - private TestLooper mLooper; - private BroadcastReceiver mBroadcastReceiver; - private ContentObserver mContentObserver; - private ScanResult mDummyNetwork; - private List<ScanDetail> mCarrierNetworks; - - - /** Initialize objects before each test run. */ - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)) - .thenReturn(mNotificationManager); - when(mFrameworkFacade.getIntegerSetting(mContext, - Settings.Global.WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(1); - when(mFrameworkFacade.getIntegerSetting(mContext, - Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, DEFAULT_REPEAT_DELAY_SEC)) - .thenReturn(DEFAULT_REPEAT_DELAY_SEC); - when(mContext.getSystemService(Context.USER_SERVICE)) - .thenReturn(mUserManager); - when(mContext.getResources()).thenReturn(mResources); - mDummyNetwork = new ScanResult(); - mDummyNetwork.SSID = TEST_SSID_1; - mDummyNetwork.capabilities = "[ESS]"; - mDummyNetwork.level = MIN_RSSI_LEVEL; - mCarrierNetworks = new ArrayList<>(); - mCarrierNetworks.add(new ScanDetail(mDummyNetwork, null /* networkDetail */)); - - mLooper = new TestLooper(); - mNotificationController = new CarrierNetworkNotifier( - mContext, mLooper.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, - mWifiConfigManager, mWifiConfigStore, mClientModeImpl, mNotificationBuilder); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - mBroadcastReceiver = broadcastReceiverCaptor.getValue(); - ArgumentCaptor<ContentObserver> observerCaptor = - ArgumentCaptor.forClass(ContentObserver.class); - verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(true), - observerCaptor.capture()); - mContentObserver = observerCaptor.getValue(); - mNotificationController.handleScreenStateChanged(true); - when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt())) - .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID)); - } - - /** - * On {@link CarrierNetworkNotifier} construction, WifiMetrics should track setting state. - */ - @Test - public void onCreate_setWifiNetworksAvailableNotificationSettingState() { - verify(mWifiMetrics).setIsWifiNetworksAvailableNotificationEnabled(CARRIER_NET_NOTIFIER_TAG, - true); - } - - /** - * When feature setting is toggled, WifiMetrics should track the disabled setting state. - */ - @Test - public void onFeatureDisable_setWifiNetworksAvailableNotificationSettingDisabled() { - when(mFrameworkFacade.getIntegerSetting(mContext, - Settings.Global.WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(0); - mContentObserver.onChange(false); - - verify(mWifiMetrics).setIsWifiNetworksAvailableNotificationEnabled(CARRIER_NET_NOTIFIER_TAG, - false); - } - - /** - * When scan results with carrier networks are handled, a notification is posted. - */ - @Test - public void handleScanResults_hasCarrierNetworks_notificationDisplayed() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - } - - /** - * When scan results with no carrier networks are handled, a notification is not posted. - */ - @Test - public void handleScanResults_emptyList_notificationNotDisplayed() { - mNotificationController.handleScanResults(new ArrayList<>()); - - verify(mNotificationManager, never()).notify(anyInt(), any()); - } - - /** - * When the feature is disabled, no notifications are posted. - */ - @Test - public void handleScanResults_featureDisabled_notificationNotDisplayed() { - when(mFrameworkFacade.getIntegerSetting(mContext, - Settings.Global.WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(0); - mContentObserver.onChange(false); - mNotificationController.handleScanResults(new ArrayList<>()); - - verify(mNotificationManager, never()).notify(anyInt(), any()); - } - - /** - * When a notification is showing and scan results with no carrier networks are handled, the - * notification is cleared. - */ - @Test - public void handleScanResults_notificationShown_emptyList_notificationCleared() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mNotificationController.handleScanResults(new ArrayList<>()); - - verify(mNotificationManager).cancel(anyInt()); - } - - /** - * When a notification is showing and no recommendation is made for the new scan results, the - * notification is cleared. - */ - @Test - public void handleScanResults_notificationShown_noRecommendation_notificationCleared() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mCarrierNetworks.clear(); - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationManager).cancel(anyInt()); - } - - /** - * When a notification is showing, screen is off, and scan results with no carrier networks are - * handled, the notification is cleared. - */ - @Test - public void handleScanResults_notificationShown_screenOff_emptyList_notificationCleared() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mNotificationController.handleScreenStateChanged(false); - mNotificationController.handleScanResults(new ArrayList<>()); - - verify(mNotificationManager).cancel(anyInt()); - } - - /** - * When {@link CarrierNetworkNotifier#clearPendingNotification(boolean)} is called and a - * notification is shown, clear the notification. - */ - @Test - public void clearPendingNotification_clearsNotificationIfOneIsShowing() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mNotificationController.clearPendingNotification(true); - - verify(mNotificationManager).cancel(anyInt()); - } - - /** - * When {@link CarrierNetworkNotifier#clearPendingNotification(boolean)} is called and a - * notification was not previously shown, do not clear the notification. - */ - @Test - public void clearPendingNotification_doesNotClearNotificationIfNoneShowing() { - mNotificationController.clearPendingNotification(true); - - verify(mNotificationManager, never()).cancel(anyInt()); - } - - /** - * When screen is off and notification is not displayed, notification is not posted on handling - * new scan results with carrier networks. - */ - @Test - public void screenOff_notificationNotShowing_handleScanResults_notificationNotDisplayed() { - mNotificationController.handleScreenStateChanged(false); - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationManager, never()).notify(anyInt(), any()); - } - - /** - * When screen is off and notification is displayed, the notification can be updated with a new - * recommendation. - */ - @Test - public void screenOff_notificationShowing_handleScanResults_recommendationCanBeUpdated() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - ScanResult newNetwork = new ScanResult(); - newNetwork.SSID = TEST_SSID_2; - mDummyNetwork.capabilities = "[ESS]"; - mDummyNetwork.level = MIN_RSSI_LEVEL + 1; - mCarrierNetworks.add(new ScanDetail(newNetwork, null /* networkDetail */)); - - mNotificationController.handleScreenStateChanged(false); - mNotificationController.handleScanResults(mCarrierNetworks); - - // Recommendation changed - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, newNetwork); - verify(mWifiMetrics).incrementNumNetworkRecommendationUpdates(CARRIER_NET_NOTIFIER_TAG); - verify(mNotificationManager, times(2)).notify(anyInt(), any()); - } - - /** - * When a notification is posted and cleared without resetting delay, the next scan with carrier - * networks should not post another notification. - */ - @Test - public void postNotification_clearNotificationWithoutDelayReset_shouldNotPostNotification() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mNotificationController.clearPendingNotification(false); - - verify(mNotificationManager).cancel(anyInt()); - - mNotificationController.handleScanResults(mCarrierNetworks); - - // no new notification posted - verify(mNotificationManager).notify(anyInt(), any()); - } - - /** - * When a notification is posted and cleared without resetting delay, the next scan with carrier - * networks should post a notification. - */ - @Test - public void postNotification_clearNotificationWithDelayReset_shouldPostNotification() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mNotificationController.clearPendingNotification(true); - - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder, times(2)).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics, times(2)).incrementConnectToNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager, times(2)).notify(anyInt(), any()); - } - - private Intent createIntent(String action) { - return new Intent(action).putExtra(AVAILABLE_NETWORK_NOTIFIER_TAG, - CARRIER_NET_NOTIFIER_TAG); - } - - /** - * When user dismissed notification and there is a recommended network, network ssid should be - * blacklisted. - */ - @Test - public void userDismissedNotification_shouldBlacklistNetwork() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_USER_DISMISSED_NOTIFICATION)); - - verify(mWifiConfigManager).saveToStore(false /* forceWrite */); - - mNotificationController.clearPendingNotification(true); - List<ScanDetail> scanResults = mCarrierNetworks; - mNotificationController.handleScanResults(scanResults); - - verify(mWifiMetrics).setNetworkRecommenderBlacklistSize(CARRIER_NET_NOTIFIER_TAG, 1); - } - - /** - * When the user chooses to connect to recommended network, network ssid should be - * blacklisted so that if the user removes the network in the future the same notification - * won't show up again. - */ - @Test - public void userConnectedNotification_shouldBlacklistNetwork() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - - verify(mWifiConfigManager).saveToStore(false /* forceWrite */); - verify(mWifiMetrics).setNetworkRecommenderBlacklistSize(CARRIER_NET_NOTIFIER_TAG, 1); - - List<ScanDetail> scanResults = mCarrierNetworks; - mNotificationController.handleScanResults(scanResults); - } - - /** - * When a notification is posted and cleared without resetting delay, after the delay has passed - * the next scan with carrier networks should post a notification. - */ - @Test - public void delaySet_delayPassed_shouldPostNotification() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mNotificationController.clearPendingNotification(false); - - // twice the delay time passed - when(mClock.getWallClockMillis()).thenReturn(DEFAULT_REPEAT_DELAY_SEC * 1000L * 2); - - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder, times(2)).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics, times(2)).incrementConnectToNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager, times(2)).notify(anyInt(), any()); - } - - /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} disables the feature. */ - @Test - public void userHasDisallowConfigWifiRestriction_notificationNotDisplayed() { - when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) - .thenReturn(true); - - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationManager, never()).notify(anyInt(), any()); - } - - /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} clears the showing notification. */ - @Test - public void userHasDisallowConfigWifiRestriction_showingNotificationIsCleared() { - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) - .thenReturn(true); - - mNotificationController.handleScanResults(mCarrierNetworks); - - verify(mNotificationManager).cancel(anyInt()); - } - - /** - * {@link ConnectToNetworkNotificationBuilder#ACTION_CONNECT_TO_NETWORK} does not connect to - * any network if the initial notification is not showing. - */ - @Test - public void actionConnectToNetwork_notificationNotShowing_doesNothing() { - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - - verify(mClientModeImpl, never()).sendMessage(any(Message.class)); - } - - /** - * {@link ConnectToNetworkNotificationBuilder#ACTION_CONNECT_TO_NETWORK} connects to the - * currently recommended network if it exists. - */ - @Test - public void actionConnectToNetwork_currentRecommendationExists_connectsAndPostsNotification() { - mNotificationController.handleScanResults(mCarrierNetworks); - - // Initial Notification - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - - verify(mClientModeImpl).sendMessage(any(Message.class)); - // Connecting Notification - verify(mNotificationBuilder).createNetworkConnectingNotification(CARRIER_NET_NOTIFIER_TAG, - mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); - verify(mWifiMetrics).incrementConnectToNetworkNotificationAction(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK, - ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); - verify(mNotificationManager, times(2)).notify(anyInt(), any()); - } - - /** - * {@link ConnectToNetworkNotificationBuilder#ACTION_PICK_WIFI_NETWORK} opens Wi-Fi settings - * if the recommendation notification is showing. - */ - @Test - public void actionPickWifiNetwork_currentRecommendationExists_opensWifiSettings() { - mNotificationController.handleScanResults(mCarrierNetworks); - - // Initial Notification - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_PICK_WIFI_NETWORK)); - - ArgumentCaptor<Intent> pickerIntentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).startActivity(pickerIntentCaptor.capture()); - assertEquals(pickerIntentCaptor.getValue().getAction(), Settings.ACTION_WIFI_SETTINGS); - verify(mWifiMetrics).incrementConnectToNetworkNotificationAction(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK, - ConnectToNetworkNotificationAndActionCount.ACTION_PICK_WIFI_NETWORK); - } - - /** - * {@link CarrierNetworkNotifier#handleWifiConnected(String ssid)} does not post connected - * notification if the connecting notification is not showing - */ - @Test - public void networkConnectionSuccess_wasNotInConnectingFlow_doesNothing() { - mNotificationController.handleWifiConnected(TEST_SSID_1); - - verify(mNotificationManager, never()).notify(anyInt(), any()); - verify(mWifiMetrics, never()).incrementConnectToNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK); - } - - /** - * {@link CarrierNetworkNotifier#handleWifiConnected(String ssid)} clears notification - * that is not connecting. - */ - @Test - public void networkConnectionSuccess_wasShowingNotification_clearsNotification() { - mNotificationController.handleScanResults(mCarrierNetworks); - - // Initial Notification - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mNotificationController.handleWifiConnected(TEST_SSID_1); - - verify(mNotificationManager).cancel(anyInt()); - } - - /** - * {@link CarrierNetworkNotifier#handleWifiConnected(String ssid)} posts the connected - * notification if the connecting notification is showing. - */ - @Test - public void networkConnectionSuccess_wasInConnectingFlow_postsConnectedNotification() { - mNotificationController.handleScanResults(mCarrierNetworks); - - // Initial Notification - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - - // Connecting Notification - verify(mNotificationBuilder).createNetworkConnectingNotification(CARRIER_NET_NOTIFIER_TAG, - mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); - verify(mWifiMetrics).incrementConnectToNetworkNotificationAction(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK, - ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); - verify(mNotificationManager, times(2)).notify(anyInt(), any()); - - mNotificationController.handleWifiConnected(TEST_SSID_1); - - // Connected Notification - verify(mNotificationBuilder).createNetworkConnectedNotification(CARRIER_NET_NOTIFIER_TAG, - mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK); - verify(mNotificationManager, times(3)).notify(anyInt(), any()); - } - - /** - * {@link CarrierNetworkNotifier#handleConnectionFailure()} posts the Failed to Connect - * notification if the connecting notification is showing. - */ - @Test - public void networkConnectionFailure_wasNotInConnectingFlow_doesNothing() { - mNotificationController.handleConnectionFailure(); - - verify(mNotificationManager, never()).notify(anyInt(), any()); - verify(mWifiMetrics, never()).incrementConnectToNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT); - } - - /** - * {@link CarrierNetworkNotifier#handleConnectionFailure()} posts the Failed to Connect - * notification if the connecting notification is showing. - */ - @Test - public void networkConnectionFailure_wasInConnectingFlow_postsFailedToConnectNotification() { - mNotificationController.handleScanResults(mCarrierNetworks); - - // Initial Notification - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - - // Connecting Notification - verify(mNotificationBuilder).createNetworkConnectingNotification(CARRIER_NET_NOTIFIER_TAG, - mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); - verify(mWifiMetrics).incrementConnectToNetworkNotificationAction(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK, - ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); - verify(mNotificationManager, times(2)).notify(anyInt(), any()); - - mNotificationController.handleConnectionFailure(); - - // Failed to Connect Notification - verify(mNotificationBuilder).createNetworkFailedNotification(CARRIER_NET_NOTIFIER_TAG); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT); - verify(mNotificationManager, times(3)).notify(anyInt(), any()); - } - - /** - * When a {@link WifiManager#CONNECT_NETWORK_FAILED} is received from the connection callback - * of {@link ClientModeImpl#sendMessage(Message)}, a Failed to Connect notification should - * be posted. On tapping this notification, Wi-Fi Settings should be launched. - */ - @Test - public void connectionFailedCallback_postsFailedToConnectNotification() throws RemoteException { - mNotificationController.handleScanResults(mCarrierNetworks); - - // Initial Notification - verify(mNotificationBuilder).createConnectToAvailableNetworkNotification( - CARRIER_NET_NOTIFIER_TAG, mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); - verify(mNotificationManager).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - - verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID, - WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER); - - ArgumentCaptor<Message> connectMessageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(connectMessageCaptor.capture()); - Message connectMessage = connectMessageCaptor.getValue(); - - // Connecting Notification - verify(mNotificationBuilder).createNetworkConnectingNotification(CARRIER_NET_NOTIFIER_TAG, - mDummyNetwork); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); - verify(mWifiMetrics).incrementConnectToNetworkNotificationAction(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK, - ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); - verify(mNotificationManager, times(2)).notify(anyInt(), any()); - - Message connectFailedMsg = Message.obtain(); - connectFailedMsg.what = WifiManager.CONNECT_NETWORK_FAILED; - connectMessage.replyTo.send(connectFailedMsg); - mLooper.dispatchAll(); - - // Failed to Connect Notification - verify(mNotificationBuilder).createNetworkFailedNotification(CARRIER_NET_NOTIFIER_TAG); - verify(mWifiMetrics).incrementConnectToNetworkNotification(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT); - verify(mWifiMetrics).incrementNumNetworkConnectMessageFailedToSend( - CARRIER_NET_NOTIFIER_TAG); - verify(mNotificationManager, times(3)).notify(anyInt(), any()); - - mBroadcastReceiver.onReceive(mContext, - createIntent(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE)); - - ArgumentCaptor<Intent> pickerIntentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).startActivity(pickerIntentCaptor.capture()); - assertEquals(pickerIntentCaptor.getValue().getAction(), Settings.ACTION_WIFI_SETTINGS); - verify(mWifiMetrics).incrementConnectToNetworkNotificationAction(CARRIER_NET_NOTIFIER_TAG, - ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT, - ConnectToNetworkNotificationAndActionCount - .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE); - } - - private List<ScanDetail> createCarrierScanResults(String... ssids) { - List<ScanDetail> scanResults = new ArrayList<>(); - for (String ssid : ssids) { - ScanResult scanResult = new ScanResult(); - scanResult.SSID = ssid; - scanResult.capabilities = "[ESS]"; - scanResults.add(new ScanDetail(scanResult, null /* networkDetail */)); - } - return scanResults; - } - - /** If list of carrier networks contain only one network, that network should be returned. */ - @Test - public void onlyNetworkIsRecommended() { - List<ScanDetail> scanResults = createCarrierScanResults(TEST_SSID_1); - scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL; - - ScanResult actual = mNotificationController.recommendNetwork(scanResults); - ScanResult expected = scanResults.get(0).getScanResult(); - assertEquals(expected, actual); - } - - /** Verifies that the network with the highest rssi is recommended. */ - @Test - public void networkWithHighestRssiIsRecommended() { - List<ScanDetail> scanResults = createCarrierScanResults(TEST_SSID_1, TEST_SSID_2); - scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL; - scanResults.get(1).getScanResult().level = MIN_RSSI_LEVEL + 1; - - ScanResult actual = mNotificationController.recommendNetwork(scanResults); - ScanResult expected = scanResults.get(1).getScanResult(); - assertEquals(expected, actual); - } - - /** - * If the best available carrier network is blacklisted, no network should be recommended. - */ - @Test - public void blacklistBestNetworkSsid_shouldNeverRecommendNetwork() { - // Add TEST_SSID_1 to blacklist - userDismissedNotification_shouldBlacklistNetwork(); - - List<ScanDetail> scanResults = createCarrierScanResults(mDummyNetwork.SSID, TEST_SSID_2); - scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL + 1; - scanResults.get(1).getScanResult().level = MIN_RSSI_LEVEL; - - ScanResult actual = mNotificationController.recommendNetwork(scanResults); - assertNull(actual); - } - - /** - * Test null input is handled - */ - @Test - public void removeNetworkFromBlacklist_handlesNull() { - mNotificationController.handleWifiConnected(null); - verify(mWifiConfigManager, never()).saveToStore(false /* forceWrite */); - } - - /** - * If the blacklist didn't change then there is no need to continue further. - */ - @Test - public void removeNetworkFromBlacklist_returnsEarlyIfNothingIsRemoved() { - mNotificationController.handleWifiConnected(TEST_SSID_1); - verify(mWifiConfigManager, never()).saveToStore(false /* forceWrite */); - } - - /** - * If we connected to a blacklisted network, then remove it from the blacklist. - */ - @Test - public void connectToNetwork_shouldRemoveSsidFromBlacklist() { - // Add TEST_SSID_1 to blacklist - userDismissedNotification_shouldBlacklistNetwork(); - - // Simulate the user connecting to TEST_SSID_1 and verify it is removed from the blacklist - mNotificationController.handleWifiConnected(mDummyNetwork.SSID); - verify(mWifiConfigManager, times(2)).saveToStore(false /* forceWrite */); - verify(mWifiMetrics).setNetworkRecommenderBlacklistSize(CARRIER_NET_NOTIFIER_TAG, 0); - ScanResult actual = mNotificationController.recommendNetwork(mCarrierNetworks); - ScanResult expected = mCarrierNetworks.get(0).getScanResult(); - assertEquals(expected, actual); - } -} diff --git a/service/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsCollectorTest.java b/service/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsCollectorTest.java deleted file mode 100644 index 8b1647fcb5..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsCollectorTest.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.telephony.TelephonyManager.NETWORK_TYPE_CDMA; -import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_0; -import static android.telephony.TelephonyManager.NETWORK_TYPE_GSM; -import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE; -import static android.telephony.TelephonyManager.NETWORK_TYPE_NR; -import static android.telephony.TelephonyManager.NETWORK_TYPE_TD_SCDMA; -import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS; -import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.validateMockitoUsage; - -import android.content.Context; -import android.telephony.CellInfo; -import android.telephony.CellInfoCdma; -import android.telephony.CellInfoGsm; -import android.telephony.CellInfoLte; -import android.telephony.CellInfoTdscdma; -import android.telephony.CellInfoWcdma; -import android.telephony.CellSignalStrengthCdma; -import android.telephony.CellSignalStrengthGsm; -import android.telephony.CellSignalStrengthLte; -import android.telephony.CellSignalStrengthNr; -import android.telephony.CellSignalStrengthTdscdma; -import android.telephony.CellSignalStrengthWcdma; -import android.telephony.SignalStrength; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.telephony.TelephonyManager.NetworkType; - -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; - -import java.util.ArrayList; -import java.util.List; - -/** - * Unit tests for {@link com.android.server.wifi.CellularLinkLayerStatsCollector}. - */ -@SmallTest -public class CellularLinkLayerStatsCollectorTest { - private CellularLinkLayerStatsCollector mCollector; - private static final String TAG = "CellCollectorTest"; - private static final int DBM_VAL = -110; - private static final int DB_VAL = -20; - private static final int DB_VAL_EVDO = 4; - private static final int SUBID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - MockitoSession mMockingSession = null; - @Mock Context mContext; - @Mock TelephonyManager mTelephonyManager; - @Mock SubscriptionManager mSubscriptionManager; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mContext.getSystemService(Context.TELEPHONY_SERVICE)) - .thenReturn(mTelephonyManager); - when(mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)) - .thenReturn(mSubscriptionManager); - when(mTelephonyManager.createForSubscriptionId(anyInt())) - .thenReturn(mTelephonyManager); - mCollector = new CellularLinkLayerStatsCollector(mContext); - mMockingSession = mockitoSession().mockStatic(SubscriptionManager.class).startMocking(); - when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(SUBID); - when(SubscriptionManager.getDefaultSubscriptionId()).thenReturn(SUBID); - } - - @After - public void cleanUp() throws Exception { - validateMockitoUsage(); - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - private List<CellInfo> generateCellInfoList(@NetworkType int networkType) { - List<CellInfo> cil = new ArrayList<>(); - int numCellInfo = 2; - for (int i = 0; i < numCellInfo; ++i) { - CellInfo ci; - if (networkType == NETWORK_TYPE_LTE) { - ci = new CellInfoLte(); - } else if (networkType == NETWORK_TYPE_CDMA || networkType == NETWORK_TYPE_EVDO_0) { - ci = new CellInfoCdma(); - } else if (networkType == NETWORK_TYPE_GSM) { - ci = new CellInfoGsm(); - } else if (networkType == NETWORK_TYPE_TD_SCDMA) { - ci = new CellInfoTdscdma(); - } else if (networkType == NETWORK_TYPE_UMTS) { - ci = new CellInfoWcdma(); - } else if (networkType == NETWORK_TYPE_NR) { - // TODO: CellInfoNr() is not supported yet. - ci = new CellInfoLte(); - } else { - ci = new CellInfoLte(); - } - if (i == 0 && networkType != NETWORK_TYPE_UNKNOWN) { - ci.setRegistered(true); - } else { - ci.setRegistered(false); - } - cil.add(ci); - } - return cil; - } - - private SignalStrength generateSignalStrength(int dBmVal, int dBVal, - @NetworkType int networkType) { - int dummy = 1000; - CellSignalStrengthLte mLte = new CellSignalStrengthLte(); - CellSignalStrengthNr mNr = new CellSignalStrengthNr(); - CellSignalStrengthGsm mGsm = new CellSignalStrengthGsm(); - CellSignalStrengthCdma mCdma = new CellSignalStrengthCdma(); - CellSignalStrengthTdscdma mTdscdma = new CellSignalStrengthTdscdma(); - CellSignalStrengthWcdma mWcdma = new CellSignalStrengthWcdma(); - - if (networkType == NETWORK_TYPE_UNKNOWN) { - return new SignalStrength(); - } else if (networkType == NETWORK_TYPE_LTE) { - mLte = new CellSignalStrengthLte(dummy, dBmVal, dBVal, dummy, dummy, dummy); - } else if (networkType == NETWORK_TYPE_CDMA) { - mCdma = new CellSignalStrengthCdma(dBmVal, dBVal, dBmVal, dummy, - SignalStrength.INVALID); - } else if (networkType == NETWORK_TYPE_EVDO_0) { - mCdma = new CellSignalStrengthCdma(dBmVal, dummy, dBmVal, dummy, dBVal); - } else if (networkType == NETWORK_TYPE_TD_SCDMA) { - mTdscdma = new CellSignalStrengthTdscdma(dummy, dummy, dBmVal); - } else if (networkType == NETWORK_TYPE_UMTS) { - mWcdma = new CellSignalStrengthWcdma(dummy, dummy, dBmVal, dBVal); - } else if (networkType == NETWORK_TYPE_GSM) { - mGsm = new CellSignalStrengthGsm(dBmVal, dummy, dummy); - } else if (networkType == NETWORK_TYPE_NR) { - mNr = new CellSignalStrengthNr(dBmVal, dummy, dBVal, dummy, dummy, dummy); - } else { - return null; - } - return new SignalStrength(mCdma, mGsm, mWcdma, mTdscdma, mLte, mNr); - } - - private void testCollectorUpdate(@NetworkType int networkType, boolean isSignalStrengthEmpty, - CellularLinkLayerStats trueStats) throws Exception { - int dBmVal = DBM_VAL; - int dBVal; - if (networkType == NETWORK_TYPE_EVDO_0) { - dBVal = DB_VAL_EVDO; - } else { - dBVal = DB_VAL; - } - - SignalStrength ss = null; - if (!isSignalStrengthEmpty) ss = generateSignalStrength(dBmVal, dBVal, networkType); - List<CellInfo> allList = generateCellInfoList(networkType); - when(mTelephonyManager.getSignalStrength()).thenReturn(ss); - when(mTelephonyManager.getAllCellInfo()).thenReturn(allList); - when(mTelephonyManager.getDataNetworkType()).thenReturn(networkType); - - CellularLinkLayerStats mStats = mCollector.update(); - - assertEquals(SUBID, SubscriptionManager.getDefaultDataSubscriptionId()); - assertEquals(SUBID, SubscriptionManager.getDefaultSubscriptionId()); - - assertEquals(trueStats.getSignalStrengthDbm(), mStats.getSignalStrengthDbm()); - assertEquals(trueStats.getSignalStrengthDb(), mStats.getSignalStrengthDb()); - assertEquals(trueStats.getDataNetworkType(), mStats.getDataNetworkType()); - assertEquals(trueStats.getIsSameRegisteredCell(), mStats.getIsSameRegisteredCell()); - } - - @Test - public void testEmptySignalStrengthLte() throws Exception { - @NetworkType int networkType; - CellularLinkLayerStats trueStats = new CellularLinkLayerStats(); - - networkType = NETWORK_TYPE_LTE; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(SignalStrength.INVALID); - trueStats.setSignalStrengthDbm(SignalStrength.INVALID); - trueStats.setDataNetworkType(NETWORK_TYPE_UNKNOWN); - testCollectorUpdate(networkType, true, trueStats); - } - - @Test - public void testRepeatCellInfoTypeTwice() throws Exception { - @NetworkType int networkType; - CellularLinkLayerStats trueStats = new CellularLinkLayerStats(); - - networkType = NETWORK_TYPE_LTE; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_LTE; - trueStats.setIsSameRegisteredCell(true); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_EVDO_0; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL_EVDO); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_EVDO_0; - trueStats.setIsSameRegisteredCell(true); - trueStats.setSignalStrengthDb(DB_VAL_EVDO); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_NR; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_NR; - trueStats.setIsSameRegisteredCell(true); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - } - - @Test - public void testLoopOverAllNetworksWithoutRepeat() throws Exception { - @NetworkType int networkType; - CellularLinkLayerStats trueStats = new CellularLinkLayerStats(); - - networkType = NETWORK_TYPE_UNKNOWN; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(SignalStrength.INVALID); - trueStats.setSignalStrengthDbm(SignalStrength.INVALID); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_LTE; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_CDMA; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_EVDO_0; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL_EVDO); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_TD_SCDMA; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(SignalStrength.INVALID); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_UMTS; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_GSM; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(SignalStrength.INVALID); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - networkType = NETWORK_TYPE_NR; - trueStats.setIsSameRegisteredCell(false); - trueStats.setSignalStrengthDb(DB_VAL); - trueStats.setSignalStrengthDbm(DBM_VAL); - trueStats.setDataNetworkType(networkType); - testCollectorUpdate(networkType, false, trueStats); - - } -} diff --git a/service/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsTest.java b/service/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsTest.java deleted file mode 100644 index 712d65355c..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.telephony.TelephonyManager; - -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Unit tests for {@link com.android.server.wifi.CellularLinkLayerStats}. - */ -@SmallTest -public class CellularLinkLayerStatsTest { - private static final String TAG = "CellularStatsTest"; - - CellularLinkLayerStats mStats; - - /** - * Sets up for unit test - */ - @Before - public void setUp() throws Exception { - mStats = new CellularLinkLayerStats(); - } - - @After - public void cleanUp() throws Exception { - } - - /** - * Test all set and get methods by checking if the inputs of set() match the output of get() - */ - @Test - public void testAllSetGetMethods() throws Exception { - int dataNetworkType = TelephonyManager.NETWORK_TYPE_GSM; - mStats.setDataNetworkType(dataNetworkType); - assertEquals(dataNetworkType, mStats.getDataNetworkType()); - int dbmVal = -100; - mStats.setSignalStrengthDbm(dbmVal); - assertEquals(dbmVal, mStats.getSignalStrengthDbm()); - int dbVal = -20; - mStats.setSignalStrengthDb(dbVal); - assertEquals(dbVal, mStats.getSignalStrengthDb()); - boolean isSameCell = true; - mStats.setIsSameRegisteredCell(isSameCell); - assertEquals(isSameCell, mStats.getIsSameRegisteredCell()); - } -} diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index 36d8441c72..efd45b1842 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -35,7 +35,6 @@ 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.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; import android.net.ConnectivityManager; import android.net.DhcpResults; @@ -48,6 +47,7 @@ import android.net.NetworkMisc; import android.net.NetworkSpecifier; import android.net.ip.IIpClient; import android.net.ip.IpClientCallbacks; +import android.net.wifi.IActionListener; import android.net.wifi.ScanResult; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; @@ -59,8 +59,6 @@ import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; -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; @@ -76,12 +74,10 @@ import android.os.Message; import android.os.Messenger; import android.os.PowerManager; import android.os.Process; -import android.os.UserHandle; import android.os.UserManager; import android.os.test.TestLooper; import android.provider.Settings; import android.security.KeyStore; -import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.test.mock.MockContentProvider; import android.test.mock.MockContentResolver; @@ -90,6 +86,7 @@ import android.util.Pair; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.internal.R; import com.android.internal.app.IBatteryStats; import com.android.internal.util.AsyncChannel; @@ -114,6 +111,8 @@ import org.mockito.ArgumentMatcher; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; @@ -122,7 +121,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -131,7 +129,7 @@ import java.util.function.Consumer; * Unit tests for {@link com.android.server.wifi.ClientModeImpl}. */ @SmallTest -public class ClientModeImplTest { +public class ClientModeImplTest extends WifiBaseTest { public static final String TAG = "ClientModeImplTest"; private static final int MANAGED_PROFILE_UID = 1100000; @@ -153,6 +151,8 @@ public class ClientModeImplTest { MacAddress.fromString("10:22:34:56:78:92"); private static final MacAddress TEST_LOCAL_MAC_ADDRESS = MacAddress.fromString("2a:53:43:c3:56:21"); + private static final MacAddress TEST_DEFAULT_MAC_ADDRESS = + MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); // NetworkAgent creates threshold ranges with Integers private static final int RSSI_THRESHOLD_MAX = -30; @@ -162,6 +162,7 @@ public class ClientModeImplTest { private static final byte RSSI_THRESHOLD_BREACH_MAX = -20; private long mBinderToken; + private MockitoSession mSession; private static <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) { return mock(class1, withSettings().extraInterfaces(interfaces)); @@ -248,6 +249,9 @@ public class ClientModeImplTest { when(context.getOpPackageName()).thenReturn(OP_PACKAGE_NAME); + when(context.getSystemService(ActivityManager.class)).thenReturn( + mock(ActivityManager.class)); + return context; } @@ -294,7 +298,8 @@ public class ClientModeImplTest { ScanDetail detail = new ScanDetail(nd, sWifiSsid, bssid, "", rssi, freq, Long.MAX_VALUE, /* needed so that scan results aren't rejected because there older than scan start */ - ie, new ArrayList<String>()); + ie, new ArrayList<String>(), ScanResults.generateIERawDatafromScanResultIE(ie)); + return detail; } @@ -337,7 +342,6 @@ public class ClientModeImplTest { MockResources mResources; FrameworkFacade mFrameworkFacade; IpClientCallbacks mIpClientCallback; - PhoneStateListener mPhoneStateListener; OsuProvider mOsuProvider; WifiConfiguration mConnectedNetwork; @@ -371,7 +375,7 @@ public class ClientModeImplTest { @Mock BaseWifiDiagnostics mWifiDiagnostics; @Mock ConnectivityManager mConnectivityManager; @Mock IProvisioningCallback mProvisioningCallback; - @Mock HandlerThread mWifiServiceHandlerThread; + @Mock HandlerThread mWifiHandlerThread; @Mock WifiPermissionsWrapper mWifiPermissionsWrapper; @Mock WakeupController mWakeupController; @Mock WifiDataStall mWifiDataStall; @@ -385,9 +389,8 @@ public class ClientModeImplTest { @Mock CarrierNetworkConfig mCarrierNetworkConfig; @Mock Handler mNetworkAgentHandler; - - final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor = - ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class); + final ArgumentCaptor<WifiConfigManager.OnNetworkUpdateListener> mConfigUpdateListenerCaptor = + ArgumentCaptor.forClass(WifiConfigManager.OnNetworkUpdateListener.class); public ClientModeImplTest() throws Exception { } @@ -403,7 +406,6 @@ public class ClientModeImplTest { /** uncomment this to enable logs from ClientModeImpls */ // enableDebugLogs(); - mWifiMonitor = new MockWifiMonitor(); when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); when(mWifiInjector.getClock()).thenReturn(new Clock()); @@ -427,8 +429,8 @@ public class ClientModeImplTest { when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager); when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager); when(mWifiInjector.getClock()).thenReturn(mClock); - when(mWifiServiceHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); - when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mWifiServiceHandlerThread); + when(mWifiHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); + when(mWifiInjector.getWifiHandlerThread()).thenReturn(mWifiHandlerThread); when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper); when(mWifiInjector.getWakeupController()).thenReturn(mWakeupController); when(mWifiInjector.getScoringParams()).thenReturn(new ScoringParams()); @@ -441,6 +443,8 @@ public class ClientModeImplTest { when(mWifiInjector.getWifiScoreCard()).thenReturn(mWifiScoreCard); when(mWifiInjector.getWifiLockManager()).thenReturn(mWifiLockManager); when(mWifiInjector.getCarrierNetworkConfig()).thenReturn(mCarrierNetworkConfig); + when(mWifiInjector.getWifiThreadRunner()) + .thenReturn(new WifiThreadRunner(new Handler(mLooper.getLooper()))); when(mWifiNetworkFactory.getSpecificNetworkRequestUidAndPackageName(any())) .thenReturn(Pair.create(Process.INVALID_UID, "")); when(mWifiNative.initialize()).thenReturn(true); @@ -455,6 +459,12 @@ public class ClientModeImplTest { return true; } }); + doAnswer(new AnswerWithArguments() { + public MacAddress answer( + WifiConfiguration config) { + return config.getRandomizedMacAddress(); + } + }).when(mWifiConfigManager).getRandomizedMacAndUpdateIfNeeded(any()); when(mWifiNative.connectToNetwork(any(), any())).thenReturn(true); when(mWifiNetworkFactory.hasConnectionRequests()).thenReturn(true); @@ -476,19 +486,6 @@ public class ClientModeImplTest { any(Context.class), any(WifiConfigManager.class), any(Handler.class))).thenReturn(mSupplicantStateTracker); - when(mUserManager.getProfileParent(11)) - .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "owner", 0)); - when(mUserManager.getProfiles(UserHandle.USER_SYSTEM)).thenReturn(Arrays.asList( - new UserInfo(UserHandle.USER_SYSTEM, "owner", 0), - new UserInfo(11, "managed profile", 0))); - - doAnswer(new AnswerWithArguments() { - public void answer(PhoneStateListener phoneStateListener, int events) - throws Exception { - mPhoneStateListener = phoneStateListener; - } - }).when(mDataTelephonyManager).listen(any(PhoneStateListener.class), anyInt()); - when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); when(mWifiPermissionsWrapper.getLocalMacAddressPermission(anyInt())) .thenReturn(PackageManager.PERMISSION_DENIED); @@ -496,6 +493,11 @@ public class ClientModeImplTest { mIpClientCallback.onQuit(); return null; }).when(mIpClient).shutdown(); + + // static mocking + mSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT) + .spyStatic(MacAddress.class) + .startMocking(); initializeCmi(); mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true); @@ -561,6 +563,9 @@ public class ClientModeImplTest { verify(mWifiNetworkFactory, atLeastOnce()).register(); verify(mUntrustedWifiNetworkFactory, atLeastOnce()).register(); + verify(mWifiConfigManager, atLeastOnce()).addOnNetworkUpdateListener( + mConfigUpdateListenerCaptor.capture()); + assertNotNull(mConfigUpdateListenerCaptor.getValue()); mLooper.startAutoDispatch(); mCmi.syncInitialize(mCmiAsyncChannel); @@ -582,6 +587,7 @@ public class ClientModeImplTest { mNetworkAgentAsyncChannel = null; mNetworkAgentHandler = null; mCmi = null; + mSession.finishMocking(); } @Test @@ -671,40 +677,13 @@ public class ClientModeImplTest { } } - private void canRemoveNetwork() { - boolean result; - when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true); - mLooper.startAutoDispatch(); - result = mCmi.syncRemoveNetwork(mCmiAsyncChannel, 0); - mLooper.stopAutoDispatch(); - - assertTrue(result); - verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt()); - } - - /** - * Verifies that configs can be removed when not in client mode. - */ - @Test - public void canRemoveNetworkConfigWhenWifiDisabled() { - canRemoveNetwork(); - } - - - /** - * Verifies that configs can be removed when in client mode. - */ - @Test - public void canRemoveNetworkConfigInClientMode() throws Exception { - initializeAndAddNetworkAndVerifySuccess(); - canRemoveNetwork(); - } - - private void canForgetNetwork() { - when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true); - mCmi.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID); + private void canForgetNetwork() throws Exception { + when(mWifiConfigManager.removeNetwork(eq(0), anyInt(), any())).thenReturn(true); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.forget(0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); mLooper.dispatchAll(); - verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt()); + verify(connectActionListener).onSuccess(); + verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt(), any()); } /** @@ -724,22 +703,22 @@ public class ClientModeImplTest { canForgetNetwork(); } - private void canSaveNetworkConfig() { + private void canSaveNetworkConfig() throws Exception { WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); int networkId = TEST_NETWORK_ID; when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) .thenReturn(new NetworkUpdateResult(networkId)); - when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt())) + when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt(), any())) .thenReturn(true); - mLooper.startAutoDispatch(); - Message reply = mCmiAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config); - mLooper.stopAutoDispatch(); - assertEquals(WifiManager.SAVE_NETWORK_SUCCEEDED, reply.what); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.save(config, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); - verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt(), any()); } /** @@ -764,15 +743,15 @@ public class ClientModeImplTest { */ @Test public void saveNetworkConfigFailsWithNullConfig() throws Exception { - mLooper.startAutoDispatch(); - Message reply = mCmiAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, null); - mLooper.stopAutoDispatch(); - assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.save(null, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onFailure(WifiManager.ERROR); verify(mWifiConfigManager, never()) .addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); verify(mWifiConfigManager, never()) - .enableNetwork(anyInt(), anyBoolean(), anyInt()); + .enableNetwork(anyInt(), anyBoolean(), anyInt(), any()); } /** @@ -785,14 +764,14 @@ public class ClientModeImplTest { when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) .thenReturn(new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID)); - mLooper.startAutoDispatch(); - Message reply = mCmiAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config); - mLooper.stopAutoDispatch(); - assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.save(config, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onFailure(WifiManager.ERROR); verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); verify(mWifiConfigManager, never()) - .enableNetwork(anyInt(), anyBoolean(), anyInt()); + .enableNetwork(anyInt(), anyBoolean(), anyInt(), any()); } /** @@ -805,16 +784,16 @@ public class ClientModeImplTest { int networkId = 5; when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) .thenReturn(new NetworkUpdateResult(networkId)); - when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt())) + when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt(), any())) .thenReturn(false); - mLooper.startAutoDispatch(); - Message reply = mCmiAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config); - mLooper.stopAutoDispatch(); - assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.save(config, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onFailure(WifiManager.ERROR); verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); - verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt(), any()); } /** @@ -831,34 +810,16 @@ public class ClientModeImplTest { assertEquals("DisconnectedState", getCurrentState().getName()); } - private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception { + private void initializeMocksForAddedNetwork(boolean isHidden) throws Exception { WifiConfiguration config = new WifiConfiguration(); config.networkId = FRAMEWORK_NETWORK_ID; config.SSID = sSSID; config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); config.hiddenSSID = isHidden; - when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) - .thenReturn(new NetworkUpdateResult(0)); when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(Arrays.asList(config)); when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); - - mLooper.startAutoDispatch(); - mCmi.syncAddOrUpdateNetwork(mCmiAsyncChannel, config); - mLooper.stopAutoDispatch(); - - verify(mWifiConfigManager).addOrUpdateNetwork(eq(config), anyInt()); - - mLooper.startAutoDispatch(); - List<WifiConfiguration> configs = mCmi.syncGetConfiguredNetworks(-1, mCmiAsyncChannel, - Process.WIFI_UID); - mLooper.stopAutoDispatch(); - assertEquals(1, configs.size()); - - WifiConfiguration config2 = configs.get(0); - assertEquals("\"GoogleGuest\"", config2.SSID); - assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)); } private void initializeAndAddNetworkAndVerifySuccess() throws Exception { @@ -867,30 +828,12 @@ public class ClientModeImplTest { private void initializeAndAddNetworkAndVerifySuccess(boolean isHidden) throws Exception { loadComponentsInStaMode(); - addNetworkAndVerifySuccess(isHidden); - } - - /** - * Helper method to retrieve WifiConfiguration by SSID. - * - * Returns the associated WifiConfiguration if it is found, null otherwise. - */ - private WifiConfiguration getWifiConfigurationForNetwork(String ssid) { - mLooper.startAutoDispatch(); - List<WifiConfiguration> configs = mCmi.syncGetConfiguredNetworks(-1, mCmiAsyncChannel, - Process.WIFI_UID); - mLooper.stopAutoDispatch(); - - for (WifiConfiguration checkConfig : configs) { - if (checkConfig.SSID.equals(ssid)) { - return checkConfig; - } - } - return null; + initializeMocksForAddedNetwork(isHidden); } private void setupAndStartConnectSequence(WifiConfiguration config) throws Exception { - when(mWifiConfigManager.enableNetwork(eq(config.networkId), eq(true), anyInt())) + when(mWifiConfigManager.enableNetwork( + eq(config.networkId), eq(true), anyInt(), any())) .thenReturn(true); when(mWifiConfigManager.updateLastConnectUid(eq(config.networkId), anyInt())) .thenReturn(true); @@ -901,13 +844,16 @@ public class ClientModeImplTest { verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); - mLooper.startAutoDispatch(); - assertTrue(mCmi.syncEnableNetwork(mCmiAsyncChannel, config.networkId, true)); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, config.networkId, mock(Binder.class), connectActionListener, 0, + Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); } private void validateSuccessfulConnectSequence(WifiConfiguration config) { - verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork( + eq(config.networkId), eq(true), anyInt(), any()); verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId)); verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); verify(mWifiConfigManager).getConfiguredNetworkWithoutMasking(eq(config.networkId)); @@ -915,7 +861,8 @@ public class ClientModeImplTest { } private void validateFailureConnectSequence(WifiConfiguration config) { - verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork( + eq(config.networkId), eq(true), anyInt(), any()); verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId)); verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); verify(mWifiConfigManager, never()) @@ -935,7 +882,7 @@ public class ClientModeImplTest { loadComponentsInStaMode(); WifiConfiguration config = mConnectedNetwork; config.networkId = FRAMEWORK_NETWORK_ID; - when(config.getOrCreateRandomizedMacAddress()).thenReturn(TEST_LOCAL_MAC_ADDRESS); + config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; setupAndStartConnectSequence(config); validateSuccessfulConnectSequence(config); @@ -956,7 +903,8 @@ public class ClientModeImplTest { when(mWifiPermissionsUtil.checkNetworkSettingsPermission(Process.myUid())) .thenReturn(false); setupAndStartConnectSequence(config); - verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork( + eq(config.networkId), eq(true), anyInt(), any()); verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(config.networkId)); verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); verify(mWifiConfigManager).getConfiguredNetworkWithoutMasking(eq(config.networkId)); @@ -1047,6 +995,9 @@ public class ClientModeImplTest { when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + // Initial value should be "not set" + assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + triggerConnect(); // CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm> @@ -1066,15 +1017,15 @@ public class ClientModeImplTest { mLooper.dispatchAll(); verify(mWifiNative).getEapAnonymousIdentity(any()); - // check that the anonymous identity remains anonymous@<realm> for subsequent connections. - assertEquals(expectedAnonymousIdentity, - mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); - // verify that WifiConfigManager#addOrUpdateNetwork() was never called if there is no - // real pseudonym to be stored. i.e. Encrypted IMSI will be always used + + // Post connection value should remain "not set" + assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + // verify that WifiConfigManager#addOrUpdateNetwork() was called to clear any previously + // stored pseudonym. i.e. to enable Encrypted IMSI for subsequent connections. // Note: This test will fail if future logic will have additional conditions that would // trigger "add or update network" operation. The test needs to be updated to account for // this change. - verify(mWifiConfigManager, never()).addOrUpdateNetwork(any(), anyInt()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); } /** @@ -1127,6 +1078,55 @@ public class ClientModeImplTest { } /** + * Tests anonymous identity is set again whenever a connection is established for the carrier + * that supports encrypted IMSI and anonymous identity but real but not decorated pseudonym was + * provided for subsequent connections. + */ + @Test + public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithNonDecoratedPseudonym() + throws Exception { + mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( + WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); + when(mDataTelephonyManager.getSimOperator()).thenReturn("123456"); + when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); + + String realm = "wlan.mnc456.mcc123.3gppnetwork.org"; + String expectedAnonymousIdentity = "anonymous"; + String pseudonym = "83bcca9384fca"; + + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + + triggerConnect(); + + // CMD_START_CONNECT should have set anonymousIdentity to anonymous@<realm> + assertEquals(expectedAnonymousIdentity + "@" + realm, + mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + + when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) + .thenReturn(mScanDetailCache); + when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( + getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); + when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( + getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); + when(mWifiNative.getEapAnonymousIdentity(anyString())) + .thenReturn(pseudonym); + + mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + verify(mWifiNative).getEapAnonymousIdentity(any()); + assertEquals(pseudonym + "@" + realm, + mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a + // real pseudonym to be stored. i.e. Encrypted IMSI will be used once, followed by + // pseudonym usage in all subsequent connections. + // Note: This test will fail if future logic will have additional conditions that would + // trigger "add or update network" operation. The test needs to be updated to account for + // this change. + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); + } + /** * Tests the Passpoint information is set in WifiInfo for Passpoint AP connection. */ @Test @@ -1136,7 +1136,6 @@ public class ClientModeImplTest { config.SSID = sWifiSsid.toString(); config.BSSID = sBSSID; config.networkId = FRAMEWORK_NETWORK_ID; - when(config.getOrCreateRandomizedMacAddress()).thenReturn(TEST_LOCAL_MAC_ADDRESS); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; setupAndStartConnectSequence(config); validateSuccessfulConnectSequence(config); @@ -1165,7 +1164,6 @@ public class ClientModeImplTest { config.SSID = sWifiSsid.toString(); config.BSSID = sBSSID; config.networkId = PASSPOINT_NETWORK_ID; - when(config.getOrCreateRandomizedMacAddress()).thenReturn(TEST_LOCAL_MAC_ADDRESS); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; setupAndStartConnectSequence(config); validateSuccessfulConnectSequence(config); @@ -1198,7 +1196,6 @@ public class ClientModeImplTest { osuConfig.osu = true; osuConfig.networkId = FRAMEWORK_NETWORK_ID; osuConfig.providerFriendlyName = WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME; - when(osuConfig.getOrCreateRandomizedMacAddress()).thenReturn(TEST_LOCAL_MAC_ADDRESS); osuConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; setupAndStartConnectSequence(osuConfig); validateSuccessfulConnectSequence(osuConfig); @@ -1229,7 +1226,6 @@ public class ClientModeImplTest { osuConfig.osu = true; osuConfig.networkId = PASSPOINT_NETWORK_ID; osuConfig.providerFriendlyName = WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME; - when(osuConfig.getOrCreateRandomizedMacAddress()).thenReturn(TEST_LOCAL_MAC_ADDRESS); osuConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; setupAndStartConnectSequence(osuConfig); validateSuccessfulConnectSequence(osuConfig); @@ -1307,7 +1303,8 @@ public class ClientModeImplTest { WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); config.networkId = FRAMEWORK_NETWORK_ID + 1; setupAndStartConnectSequence(config); - verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork( + eq(config.networkId), eq(true), anyInt(), any()); verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(config.networkId)); verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); verify(mWifiConfigManager, never()) @@ -1323,11 +1320,12 @@ public class ClientModeImplTest { verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); - mLooper.startAutoDispatch(); - assertFalse(mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true)); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onFailure(anyInt()); - verify(mWifiConfigManager, never()).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager, never()).enableNetwork(eq(0), eq(true), anyInt(), any()); verify(mWifiConfigManager, never()).updateLastConnectUid(eq(0), anyInt()); } @@ -1339,16 +1337,62 @@ public class ClientModeImplTest { * that connection request returns with CONNECT_NETWORK_SUCCEEDED. */ @Test - public void reconnectToConnectedNetwork() throws Exception { + public void reconnectToConnectedNetworkWithNetworkId() throws Exception { + connect(); + + // try to reconnect + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, FRAMEWORK_NETWORK_ID, mock(Binder.class), connectActionListener, 0, + Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); + + // Verify that we didn't trigger a second connection. + verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); + } + + /** + * 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 reconnectToConnectedNetworkWithConfig() throws Exception { + connect(); + + // try to reconnect + WifiConfiguration config = new WifiConfiguration(); + config.networkId = FRAMEWORK_NETWORK_ID; + when(mWifiConfigManager.addOrUpdateNetwork(eq(config), anyInt())) + .thenReturn(new NetworkUpdateResult(FRAMEWORK_NETWORK_ID)); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(config, WifiConfiguration.INVALID_NETWORK_ID, mock(Binder.class), + connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); + + // Verify that we didn't trigger a second connection. + verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), any()); + } + + /** + * If caller tries to connect to a new network while still provisioning the current one, + * the connection attempt should succeed. + */ + @Test + public void connectWhileObtainingIp() throws Exception { initializeAndAddNetworkAndVerifySuccess(); verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); mLooper.dispatchAll(); @@ -1358,13 +1402,22 @@ public class ClientModeImplTest { mLooper.dispatchAll(); assertEquals("ObtainingIpState", getCurrentState().getName()); + reset(mWifiNative); - // try to reconnect - mLooper.startAutoDispatch(); - Message reply = mCmiAsyncChannel.sendMessageSynchronously(WifiManager.CONNECT_NETWORK, 0); - mLooper.stopAutoDispatch(); + // Connect to a different network + WifiConfiguration config = new WifiConfiguration(); + config.networkId = TEST_NETWORK_ID; + when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config); + + mCmi.connect(null, TEST_NETWORK_ID, mock(Binder.class), null, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + + mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED)); + mLooper.dispatchAll(); + + verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); - assertEquals(WifiManager.CONNECT_NETWORK_SUCCEEDED, reply.what); } /** @@ -1373,19 +1426,16 @@ public class ClientModeImplTest { @Test public void testManualConnectNominator() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - Message msg = Message.obtain(); - msg.what = WifiManager.CONNECT_NETWORK; - msg.arg1 = TEST_NETWORK_ID; - msg.obj = null; - msg.sendingUid = Process.SYSTEM_UID; WifiConfiguration config = new WifiConfiguration(); config.networkId = TEST_NETWORK_ID; - when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config); - mCmi.sendMessage(msg); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, TEST_NETWORK_ID, mock(Binder.class), connectActionListener, 0, + Process.SYSTEM_UID); mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID, WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL); @@ -1395,11 +1445,12 @@ public class ClientModeImplTest { public void testDhcpFailure() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); mLooper.dispatchAll(); @@ -1429,11 +1480,12 @@ public class ClientModeImplTest { public void testWrongPasswordWithPreviouslyConnected() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); WifiConfiguration config = new WifiConfiguration(); config.getNetworkSelectionStatus().setHasEverConnected(true); @@ -1459,11 +1511,12 @@ public class ClientModeImplTest { public void testWrongPasswordWithNeverConnected() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); WifiConfiguration config = new WifiConfiguration(); config.SSID = sSSID; @@ -1489,11 +1542,12 @@ public class ClientModeImplTest { public void testWrongPasswordWithNullNetwork() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); @@ -1517,11 +1571,12 @@ public class ClientModeImplTest { public void testEapSimErrorVendorSpecific() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); WifiConfiguration config = new WifiConfiguration(); config.getNetworkSelectionStatus().setHasEverConnected(true); @@ -1545,11 +1600,12 @@ public class ClientModeImplTest { public void testEapTlsErrorVendorSpecific() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); WifiConfiguration config = new WifiConfiguration(); config.getNetworkSelectionStatus().setHasEverConnected(true); @@ -1572,11 +1628,12 @@ public class ClientModeImplTest { public void testEapSimNoSubscribedError() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); @@ -1594,11 +1651,12 @@ public class ClientModeImplTest { public void testBadNetworkEvent() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - mLooper.startAutoDispatch(); - mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID); mLooper.dispatchAll(); @@ -1823,108 +1881,11 @@ public class ClientModeImplTest { } /** - * 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, - OP_PACKAGE_NAME)).thenReturn(true); - mLooper.startAutoDispatch(); - assertTrue(mCmi.syncAddOrUpdatePasspointConfig( - mCmiAsyncChannel, config, MANAGED_PROFILE_UID, OP_PACKAGE_NAME)); - mLooper.stopAutoDispatch(); - reset(mPasspointManager); - - when(mPasspointManager.addOrUpdateProvider(config, MANAGED_PROFILE_UID, - OP_PACKAGE_NAME)).thenReturn(false); - mLooper.startAutoDispatch(); - assertFalse(mCmi.syncAddOrUpdatePasspointConfig( - mCmiAsyncChannel, config, MANAGED_PROFILE_UID, OP_PACKAGE_NAME)); - 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(anyInt(), anyBoolean(), eq(fqdn))).thenReturn(true); - mLooper.startAutoDispatch(); - assertTrue(mCmi.syncRemovePasspointConfig(mCmiAsyncChannel, true, fqdn)); - mLooper.stopAutoDispatch(); - reset(mPasspointManager); - - when(mPasspointManager.removeProvider(anyInt(), anyBoolean(), eq(fqdn))).thenReturn(false); - mLooper.startAutoDispatch(); - assertFalse(mCmi.syncRemovePasspointConfig(mCmiAsyncChannel, true, 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(anyInt(), anyBoolean())) - .thenReturn(expectedConfigs); - mLooper.startAutoDispatch(); - assertEquals(expectedConfigs, mCmi.syncGetPasspointConfigs(mCmiAsyncChannel, true)); - mLooper.stopAutoDispatch(); - reset(mPasspointManager); - - when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean())) - .thenReturn(new ArrayList<>()); - mLooper.startAutoDispatch(); - assertTrue(mCmi.syncGetPasspointConfigs(mCmiAsyncChannel, true).isEmpty()); - mLooper.stopAutoDispatch(); - } - - /** * Verify that syncStartSubscriptionProvisioning will redirect calls with right parameters * to {@link PasspointManager} with expected true being returned when in client mode. */ @Test public void syncStartSubscriptionProvisioningInClientMode() throws Exception { - // syncInitialize is invoke in Setup. - verify(mPasspointManager).initializeProvisioner(any(Looper.class)); - loadComponentsInStaMode(); when(mPasspointManager.startSubscriptionProvisioning(anyInt(), any(OsuProvider.class), any(IProvisioningCallback.class))).thenReturn(true); @@ -1972,16 +1933,18 @@ public class ClientModeImplTest { public void disconnectFromNetworkWhenRemovedWhileObtainingIpAddr() throws Exception { initializeAndAddNetworkAndVerifySuccess(); - when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(true); + when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt(), any())) + .thenReturn(true); when(mWifiConfigManager.updateLastConnectUid(eq(0), anyInt())).thenReturn(true); verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); - mLooper.startAutoDispatch(); - assertTrue(mCmi.syncEnableNetwork(mCmiAsyncChannel, 0, true)); - mLooper.stopAutoDispatch(); + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); verify(mWifiConnectivityManager).setUserConnectChoice(eq(0)); when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) .thenReturn(mScanDetailCache); @@ -2001,11 +1964,18 @@ public class ClientModeImplTest { assertEquals("ObtainingIpState", getCurrentState().getName()); // now remove the config - when(mWifiConfigManager.removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt())) + reset(connectActionListener); + when(mWifiConfigManager.removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt(), any())) .thenReturn(true); - mCmi.sendMessage(WifiManager.FORGET_NETWORK, FRAMEWORK_NETWORK_ID, MANAGED_PROFILE_UID); + mCmi.forget(FRAMEWORK_NETWORK_ID, mock(Binder.class), connectActionListener, 0, + Binder.getCallingUid()); mLooper.dispatchAll(); - verify(mWifiConfigManager).removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt()); + verify(connectActionListener).onSuccess(); + verify(mWifiConfigManager).removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt(), any()); + // trigger removal callback to trigger disconnect. + WifiConfiguration removedConfig = new WifiConfiguration(); + removedConfig.networkId = FRAMEWORK_NETWORK_ID; + mConfigUpdateListenerCaptor.getValue().onNetworkRemoved(removedConfig); reset(mWifiConfigManager); @@ -2101,7 +2071,7 @@ public class ClientModeImplTest { // This simulates the behavior of roaming to network with |sBSSID1|, |sFreq1|. // Send a CMD_ASSOCIATED_BSSID, verify WifiInfo is updated. - mCmi.sendMessage(ClientModeImpl.CMD_ASSOCIATED_BSSID, 0, 0, sBSSID1); + mCmi.sendMessage(WifiMonitor.ASSOCIATED_BSSID_EVENT, 0, 0, sBSSID1); mLooper.dispatchAll(); WifiInfo wifiInfo = mCmi.getWifiInfo(); @@ -2133,7 +2103,6 @@ public class ClientModeImplTest { assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true)); inOrder.verify(mWifiNetworkFactory).setWifiState(eq(true)); - inOrderSarMgr.verify(mSarManager).setClientWifiState(WifiManager.WIFI_STATE_ENABLED); inOrderMetrics.verify(mWifiMetrics) .setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); inOrderMetrics.verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_WIFI_ENABLED); @@ -2157,7 +2126,6 @@ public class ClientModeImplTest { assertEquals(WifiManager.WIFI_STATE_DISABLED, mCmi.syncGetWifiState()); inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(false)); inOrder.verify(mWifiNetworkFactory).setWifiState(eq(false)); - inOrderSarMgr.verify(mSarManager).setClientWifiState(WifiManager.WIFI_STATE_DISABLED); inOrderMetrics.verify(mWifiMetrics).setWifiState(WifiMetricsProto.WifiLog.WIFI_DISABLED); inOrderMetrics.verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_WIFI_DISABLED); assertNull(wifiInfo.getBSSID()); @@ -2181,7 +2149,6 @@ public class ClientModeImplTest { assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true)); inOrder.verify(mWifiNetworkFactory).setWifiState(eq(true)); - inOrderSarMgr.verify(mSarManager).setClientWifiState(WifiManager.WIFI_STATE_ENABLED); inOrderMetrics.verify(mWifiMetrics) .setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); inOrderMetrics.verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_WIFI_ENABLED); @@ -2247,38 +2214,11 @@ public class ClientModeImplTest { public void addNetworkInDefaultState() throws Exception { // We should not be in initial state now. assertTrue("DefaultState".equals(getCurrentState().getName())); - addNetworkAndVerifySuccess(false); + initializeMocksForAddedNetwork(false); verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(0)); } /** - * Test that DISABLE_NETWORK returns failure to public API when WifiConfigManager returns - * failure. - */ - @Test - public void testSyncDisableNetwork_failure() throws Exception { - loadComponentsInStaMode(); - when(mWifiConfigManager.disableNetwork(anyInt(), anyInt())).thenReturn(false); - - mLooper.startAutoDispatch(); - boolean succeeded = mCmi.syncDisableNetwork(mCmiAsyncChannel, 0); - mLooper.stopAutoDispatch(); - assertFalse(succeeded); - } - - /** - * Test that we don't register the telephony call state listener on devices which do not support - * setting/resetting Tx power limit. - */ - @Test - public void testVoiceCallSar_disabledTxPowerScenario_WifiOn() throws Exception { - loadComponentsInStaMode(); - assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); - assertEquals("DisconnectedState", getCurrentState().getName()); - assertNull(mPhoneStateListener); - } - - /** * Verifies that ClientModeImpl sets and unsets appropriate 'RecentFailureReason' values * on a WifiConfiguration when it fails association, authentication, or successfully connects */ @@ -2577,8 +2517,6 @@ public class ClientModeImplTest { assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); connect(); - verify(mWifiConfigManager, never()).setNetworkRandomizedMacAddress(0, - TEST_LOCAL_MAC_ADDRESS); verify(mWifiNative, never()).setMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); verify(mWifiMetrics, never()) .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); @@ -2602,7 +2540,6 @@ public class ClientModeImplTest { assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); connect(); - verify(mWifiConfigManager).setNetworkRandomizedMacAddress(0, TEST_LOCAL_MAC_ADDRESS); verify(mWifiNative).setMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); verify(mWifiMetrics) .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); @@ -2627,7 +2564,6 @@ public class ClientModeImplTest { .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); connect(); - verify(mWifiConfigManager).setNetworkRandomizedMacAddress(0, TEST_LOCAL_MAC_ADDRESS); verify(mWifiNative, never()).setMacAddress(WIFI_IFACE_NAME, TEST_LOCAL_MAC_ADDRESS); verify(mWifiMetrics, never()) .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); @@ -2661,12 +2597,10 @@ public class ClientModeImplTest { mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); mLooper.dispatchAll(); - verify(mWifiConfigManager, never()).setNetworkRandomizedMacAddress(anyInt(), any()); verify(mWifiNative).setMacAddress(WIFI_IFACE_NAME, TEST_GLOBAL_MAC_ADDRESS); verify(mWifiMetrics) .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); - verify(config, never()).getOrCreateRandomizedMacAddress(); } /** @@ -2693,12 +2627,10 @@ public class ClientModeImplTest { mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); mLooper.dispatchAll(); - verify(mWifiConfigManager, never()).setNetworkRandomizedMacAddress(anyInt(), any()); verify(mWifiNative, never()).setMacAddress(WIFI_IFACE_NAME, TEST_GLOBAL_MAC_ADDRESS); verify(mWifiMetrics, never()) .logStaEvent(eq(StaEvent.TYPE_MAC_CHANGE), any(WifiConfiguration.class)); assertEquals(TEST_GLOBAL_MAC_ADDRESS.toString(), mCmi.getWifiInfo().getMacAddress()); - verify(config, never()).getOrCreateRandomizedMacAddress(); } /** @@ -2735,8 +2667,7 @@ public class ClientModeImplTest { WifiConfiguration config = mock(WifiConfiguration.class); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; - when(config.getOrCreateRandomizedMacAddress()) - .thenReturn(MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS)); + config.setRandomizedMacAddress(MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS)); when(config.getNetworkSelectionStatus()) .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); @@ -2744,7 +2675,6 @@ public class ClientModeImplTest { mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); mLooper.dispatchAll(); - verify(config).getOrCreateRandomizedMacAddress(); verify(mWifiNative, never()).setMacAddress(eq(WIFI_IFACE_NAME), any(MacAddress.class)); } @@ -3555,23 +3485,6 @@ public class ClientModeImplTest { } /** - * Verify removing Passpoint configuration will also remove the WifiConfiguration for it. - */ - @Test - public void testRemovePasspointConfig() throws Exception { - String fqdn = "test.com"; - when(mPasspointManager.removeProvider(anyInt(), anyBoolean(), anyString())) - .thenReturn(true); - - // switch to connect mode and verify wifi is reported as enabled - startSupplicantAndDispatchMessages(); - mCmi.sendMessage(ClientModeImpl.CMD_REMOVE_PASSPOINT_CONFIG, TEST_UID, 0, fqdn); - mLooper.dispatchAll(); - - verify(mWifiConfigManager).removePasspointConfiguredNetwork(fqdn); - } - - /** * Verify removing sim will also remove an ephemeral Passpoint Provider. */ @Test @@ -3667,64 +3580,131 @@ public class ClientModeImplTest { } /** - * Verify that syncGetAllMatchingFqdnsForScanResults does not return null from a null message. + * Tests that when {@link ClientModeImpl} receives a SUP_REQUEST_IDENTITY message, it responds + * to the supplicant with the SIM identity. */ @Test - public void testSyncGetAllMatchingFqdnsForScanResult_doesNotReturnNull() { - assertNotNull( - mCmi.syncGetAllMatchingFqdnsForScanResults(null, mNullAsyncChannel)); + public void testSupRequestIdentity_setsIdentityResponse() throws Exception { + mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( + WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); + mConnectedNetwork.SSID = DEFAULT_TEST_SSID; + + when(mDataTelephonyManager.getSubscriberId()).thenReturn("3214561234567890"); + when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + when(mDataTelephonyManager.getSimOperator()).thenReturn("321456"); + when(mDataTelephonyManager.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); + + triggerConnect(); + + mCmi.sendMessage(WifiMonitor.SUP_REQUEST_IDENTITY, + 0, FRAMEWORK_NETWORK_ID, DEFAULT_TEST_SSID); + mLooper.dispatchAll(); + + verify(mWifiNative).simIdentityResponse(WIFI_IFACE_NAME, + "13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", ""); } /** - * Verify that syncGetMatchingOsuProviders does not return null from a null message. + * Verifies that WifiLastResortWatchdog is notified of DHCP failures when recevied + * NETWORK_DISCONNECTION_EVENT while in ObtainingIpState. */ @Test - public void testSyncGetMatchingOsuProviders_doesNotReturnNull() { - assertNotNull( - mCmi.syncGetMatchingOsuProviders(null, mNullAsyncChannel)); + public void testDhcpFailureUpdatesWatchdog_WhenDisconnectedWhileObtainingIpAddr() + throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + + verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); + + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); + + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); + + mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); + mLooper.dispatchAll(); + + assertEquals("ObtainingIpState", getCurrentState().getName()); + + // Verifies that WifiLastResortWatchdog be notified. + mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + assertEquals("DisconnectedState", getCurrentState().getName()); + verify(mWifiLastResortWatchdog).noteConnectionFailureAndTriggerIfNeeded( + sSSID, sBSSID, WifiLastResortWatchdog.FAILURE_CODE_DHCP); } /** - * Verify that syncGetMatchingPasspointConfigsForOsuProviders does not return null from a null - * message. + * Verifies that we trigger a disconnect when the {@link WifiConfigManager. + * OnNetworkUpdateListener#onNetworkRemoved(WifiConfiguration)} is invoked. */ @Test - public void testSyncGetMatchingPasspointConfigsForOsuProviders_doesNotReturnNull() { - assertNotNull( - mCmi.syncGetMatchingPasspointConfigsForOsuProviders(null, mNullAsyncChannel)); + public void testOnNetworkRemoved() throws Exception { + connect(); + + WifiConfiguration removedNetwork = new WifiConfiguration(); + removedNetwork.networkId = FRAMEWORK_NETWORK_ID; + mConfigUpdateListenerCaptor.getValue().onNetworkRemoved(removedNetwork); + mLooper.dispatchAll(); + + verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); + assertEquals("DisconnectingState", getCurrentState().getName()); } /** - * Verify that syncGetWifiConfigsForPasspointProfiles does not return null from a null message. + * Verifies that we trigger a disconnect when the {@link WifiConfigManager + * .OnNetworkUpdateListener#onNetworkPermanentlyDisabled(WifiConfiguration, int)} is invoked. */ @Test - public void testSyncGetWifiConfigsForPasspointProfiles_doesNotReturnNull() { - assertNotNull( - mCmi.syncGetWifiConfigsForPasspointProfiles(null, mNullAsyncChannel)); + public void testOnNetworkPermanentlyDisabled() throws Exception { + connect(); + + WifiConfiguration disabledNetwork = new WifiConfiguration(); + disabledNetwork.networkId = FRAMEWORK_NETWORK_ID; + mConfigUpdateListenerCaptor.getValue().onNetworkPermanentlyDisabled(disabledNetwork, + WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD); + mLooper.dispatchAll(); + + assertEquals("DisconnectingState", getCurrentState().getName()); } /** - * Tests that when {@link ClientModeImpl} receives a SUP_REQUEST_IDENTITY message, it responds - * to the supplicant with the SIM identity. + * Verifies that we don't trigger a disconnect when the {@link WifiConfigManager + * .OnNetworkUpdateListener#onNetworkPermanentlyDisabled(WifiConfiguration, int)} is invoked. */ @Test - public void testSupRequestIdentity_setsIdentityResponse() throws Exception { - mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( - WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); - mConnectedNetwork.SSID = DEFAULT_TEST_SSID; + public void testOnNetworkPermanentlyDisabledWithNoInternet() throws Exception { + connect(); - when(mDataTelephonyManager.getSubscriberId()).thenReturn("3214561234567890"); - when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); - when(mDataTelephonyManager.getSimOperator()).thenReturn("321456"); - when(mDataTelephonyManager.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); + WifiConfiguration disabledNetwork = new WifiConfiguration(); + disabledNetwork.networkId = FRAMEWORK_NETWORK_ID; + mConfigUpdateListenerCaptor.getValue().onNetworkPermanentlyDisabled(disabledNetwork, + WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT); + mLooper.dispatchAll(); - triggerConnect(); + assertEquals("ConnectedState", getCurrentState().getName()); + } - mCmi.sendMessage(WifiMonitor.SUP_REQUEST_IDENTITY, - 0, FRAMEWORK_NETWORK_ID, DEFAULT_TEST_SSID); + /** + * Verifies that we don't trigger a disconnect when the {@link WifiConfigManager + * .OnNetworkUpdateListener#onNetworkTemporarilyDisabled(WifiConfiguration, int)} is invoked. + */ + @Test + public void testOnNetworkTemporarilyDisabledWithNoInternet() throws Exception { + connect(); + + WifiConfiguration disabledNetwork = new WifiConfiguration(); + disabledNetwork.networkId = FRAMEWORK_NETWORK_ID; + mConfigUpdateListenerCaptor.getValue().onNetworkTemporarilyDisabled(disabledNetwork, + WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY); mLooper.dispatchAll(); - verify(mWifiNative).simIdentityResponse(WIFI_IFACE_NAME, FRAMEWORK_NETWORK_ID, - "13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", ""); + assertEquals("ConnectedState", getCurrentState().getName()); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java index 867b8ab09e..e94d307b93 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java @@ -17,9 +17,7 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_STATE; -import static android.net.wifi.WifiManager.EXTRA_SCAN_AVAILABLE; import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE; -import static android.net.wifi.WifiManager.WIFI_SCAN_AVAILABLE; import static android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; @@ -28,6 +26,7 @@ import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; import android.content.Context; @@ -40,6 +39,7 @@ import android.util.Log; 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,7 +49,7 @@ import java.util.List; * Unit tests for {@link ClientModeManager}. */ @SmallTest -public class ClientModeManagerTest { +public class ClientModeManagerTest extends WifiBaseTest { private static final String TAG = "ClientModeManagerTest"; private static final String TEST_INTERFACE_NAME = "testif0"; private static final String OTHER_INTERFACE_NAME = "notTestIf"; @@ -62,7 +62,8 @@ public class ClientModeManagerTest { @Mock WifiMetrics mWifiMetrics; @Mock WifiNative mWifiNative; @Mock ClientModeManager.Listener mListener; - @Mock WifiMonitor mWifiMonitor; + @Mock SarManager mSarManager; + @Mock WakeupController mWakeupController; @Mock ClientModeImpl mClientModeImpl; final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor = @@ -79,44 +80,72 @@ public class ClientModeManagerTest { private ClientModeManager createClientModeManager() { return new ClientModeManager(mContext, mLooper.getLooper(), mWifiNative, mListener, - mWifiMetrics, mClientModeImpl); + mWifiMetrics, mSarManager, mWakeupController, mClientModeImpl); } - private void startClientModeAndVerifyEnabled() throws Exception { - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + private void startClientInScanOnlyModeAndVerifyEnabled() throws Exception { + when(mWifiNative.setupInterfaceForClientInScanMode(any())) + .thenReturn(TEST_INTERFACE_NAME); + mClientModeManager.start(); + mLooper.dispatchAll(); + + verify(mWifiNative).setupInterfaceForClientInScanMode( + mInterfaceCallbackCaptor.capture()); + verify(mClientModeImpl).setOperationalMode( + ClientModeImpl.SCAN_ONLY_MODE, TEST_INTERFACE_NAME); + verify(mSarManager).setScanOnlyWifiState(WIFI_STATE_ENABLED); + + // now mark the interface as up + mInterfaceCallbackCaptor.getValue().onUp(TEST_INTERFACE_NAME); + mLooper.dispatchAll(); + + // Ensure that no public broadcasts were sent. + verifyNoMoreInteractions(mContext); + verify(mListener).onStarted(); + } - when(mWifiNative.setupInterfaceForClientInConnectivityMode(any())) + private void startClientInConnectModeAndVerifyEnabled() throws Exception { + when(mWifiNative.setupInterfaceForClientInScanMode(any())) .thenReturn(TEST_INTERFACE_NAME); + when(mWifiNative.switchClientInterfaceToConnectivityMode(any())) + .thenReturn(true); mClientModeManager.start(); mLooper.dispatchAll(); - verify(mWifiNative).setupInterfaceForClientInConnectivityMode( + verify(mWifiNative).setupInterfaceForClientInScanMode( mInterfaceCallbackCaptor.capture()); + verify(mClientModeImpl).setOperationalMode( + ClientModeImpl.SCAN_ONLY_MODE, TEST_INTERFACE_NAME); + mLooper.dispatchAll(); + + mClientModeManager.switchToConnectMode(); + mLooper.dispatchAll(); + + verify(mWifiNative).switchClientInterfaceToConnectivityMode(TEST_INTERFACE_NAME); + verify(mClientModeImpl).setOperationalMode( + ClientModeImpl.CONNECT_MODE, TEST_INTERFACE_NAME); + verify(mSarManager).setClientWifiState(WIFI_STATE_ENABLED); // now mark the interface as up mInterfaceCallbackCaptor.getValue().onUp(TEST_INTERFACE_NAME); mLooper.dispatchAll(); + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, atLeastOnce()).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); List<Intent> intents = intentCaptor.getAllValues(); assertEquals(2, intents.size()); Log.d(TAG, "captured intents: " + intents); - checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, WIFI_STATE_DISABLED); - checkWifiStateChangedBroadcast(intents.get(1), WIFI_STATE_ENABLED, WIFI_STATE_ENABLING); + checkWifiConnectModeStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, + WIFI_STATE_DISABLED); + checkWifiConnectModeStateChangedBroadcast(intents.get(1), WIFI_STATE_ENABLED, + WIFI_STATE_ENABLING); - checkWifiStateChangeListenerUpdate(WIFI_STATE_ENABLED); + verify(mListener, times(2)).onStarted(); } - private void checkWifiScanStateChangedBroadcast(Intent intent, int expectedCurrentState) { - String action = intent.getAction(); - assertEquals(WIFI_SCAN_AVAILABLE, action); - int currentState = intent.getIntExtra(EXTRA_SCAN_AVAILABLE, WIFI_STATE_UNKNOWN); - assertEquals(expectedCurrentState, currentState); - } - - private void checkWifiStateChangedBroadcast( + private void checkWifiConnectModeStateChangedBroadcast( Intent intent, int expectedCurrentState, int expectedPrevState) { String action = intent.getAction(); assertEquals(WIFI_STATE_CHANGED_ACTION, action); @@ -128,80 +157,135 @@ public class ClientModeManagerTest { verify(mClientModeImpl, atLeastOnce()).setWifiStateForApiCalls(expectedCurrentState); } - - private void checkWifiStateChangeListenerUpdate(int expectedCurrentState) { - verify(mListener).onStateChanged(eq(expectedCurrentState)); - } - - private void verifyNotificationsForCleanShutdown(int fromState) { + private void verifyConnectModeNotificationsForCleanShutdown(int fromState) { ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, atLeastOnce()) .sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); List<Intent> intents = intentCaptor.getAllValues(); - assertEquals(2, intents.size()); - checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_DISABLING, fromState); - checkWifiStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED, WIFI_STATE_DISABLING); + assertTrue(intents.size() >= 2); + checkWifiConnectModeStateChangedBroadcast(intents.get(intents.size() - 2), + WIFI_STATE_DISABLING, fromState); + checkWifiConnectModeStateChangedBroadcast(intents.get(intents.size() - 1), + WIFI_STATE_DISABLED, WIFI_STATE_DISABLING); } - private void verifyNotificationsForFailure() { + private void verifyConnectModeNotificationsForFailure() { ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, atLeastOnce()) .sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); List<Intent> intents = intentCaptor.getAllValues(); assertEquals(2, intents.size()); - checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_DISABLING, WIFI_STATE_UNKNOWN); - checkWifiStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED, WIFI_STATE_DISABLING); - checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN); + checkWifiConnectModeStateChangedBroadcast(intents.get(0), WIFI_STATE_DISABLING, + WIFI_STATE_UNKNOWN); + checkWifiConnectModeStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED, + WIFI_STATE_DISABLING); } /** * ClientMode start sets up an interface in ClientMode. */ @Test - public void clientModeStartCreatesClientInterface() throws Exception { - startClientModeAndVerifyEnabled(); + public void clientInConnectModeStartCreatesClientInterface() throws Exception { + startClientInConnectModeAndVerifyEnabled(); } /** - * ClientMode increments failure metrics when failing to setup client mode. + * ClientMode start sets up an interface in ClientMode. */ @Test - public void detectAndReportErrorWhenSetupForClientWifiNativeFailure() throws Exception { - when(mWifiNative.setupInterfaceForClientInConnectivityMode(any())).thenReturn(null); - mClientModeManager.start(); + public void clientInScanOnlyModeStartCreatesClientInterface() throws Exception { + startClientInScanOnlyModeAndVerifyEnabled(); + } + + /** + * Switch ClientModeManager from ScanOnly mode To Connect mode. + */ + @Test + public void switchFromScanOnlyModeToConnectMode() throws Exception { + startClientInScanOnlyModeAndVerifyEnabled(); + + when(mWifiNative.switchClientInterfaceToConnectivityMode(any())) + .thenReturn(true); + mClientModeManager.switchToConnectMode(); mLooper.dispatchAll(); + verify(mSarManager).setScanOnlyWifiState(WIFI_STATE_DISABLED); + verify(mClientModeImpl).setOperationalMode( + ClientModeImpl.SCAN_ONLY_MODE, TEST_INTERFACE_NAME); + verify(mClientModeImpl).setOperationalMode( + ClientModeImpl.CONNECT_MODE, TEST_INTERFACE_NAME); + verify(mSarManager).setClientWifiState(WIFI_STATE_ENABLED); + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, atLeastOnce()).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); + List<Intent> intents = intentCaptor.getAllValues(); assertEquals(2, intents.size()); - checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, WIFI_STATE_DISABLED); - checkWifiStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED, WIFI_STATE_UNKNOWN); - checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN); + Log.d(TAG, "captured intents: " + intents); + checkWifiConnectModeStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, + WIFI_STATE_DISABLED); + checkWifiConnectModeStateChangedBroadcast(intents.get(1), WIFI_STATE_ENABLED, + WIFI_STATE_ENABLING); + + verify(mListener, times(2)).onStarted(); } /** - * ClientMode start does not indicate scanning is available when the interface name is empty. + * Switch ClientModeManager from Connect mode to ScanOnly mode. */ @Test - public void clientModeStartDoesNotSendScanningActiveWhenClientInterfaceNameIsEmpty() + public void switchFromConnectModeToScanOnlyMode() throws Exception { + startClientInConnectModeAndVerifyEnabled(); + + when(mWifiNative.switchClientInterfaceToScanMode(any())) + .thenReturn(true); + mClientModeManager.switchToScanOnlyMode(); + mLooper.dispatchAll(); + + verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED); + + verify(mSarManager).setClientWifiState(WIFI_STATE_DISABLED); + verify(mWifiNative).setupInterfaceForClientInScanMode( + mInterfaceCallbackCaptor.capture()); + verify(mWifiNative).switchClientInterfaceToScanMode(TEST_INTERFACE_NAME); + verify(mClientModeImpl, times(2)).setOperationalMode( + ClientModeImpl.SCAN_ONLY_MODE, TEST_INTERFACE_NAME); + verify(mSarManager, times(2)).setScanOnlyWifiState(WIFI_STATE_ENABLED); + + // Ensure that no public broadcasts were sent. + verifyNoMoreInteractions(mContext); + verify(mListener, times(3)).onStarted(); + } + + /** + * ClientMode increments failure metrics when failing to setup client mode in connectivity mode. + */ + @Test + public void detectAndReportErrorWhenSetupForClientInConnectivityModeWifiNativeFailure() throws Exception { - when(mWifiNative.setupInterfaceForClientInConnectivityMode(any())).thenReturn(""); + when(mWifiNative.setupInterfaceForClientInScanMode(any())) + .thenReturn(TEST_INTERFACE_NAME); + when(mWifiNative.switchClientInterfaceToConnectivityMode(any())).thenReturn(false); + mClientModeManager.start(); mLooper.dispatchAll(); + mClientModeManager.switchToConnectMode(); + mLooper.dispatchAll(); + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, atLeastOnce()).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); - List<Intent> intents = intentCaptor.getAllValues(); assertEquals(2, intents.size()); - checkWifiStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, WIFI_STATE_DISABLED); - checkWifiStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED, WIFI_STATE_UNKNOWN); - checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN); + checkWifiConnectModeStateChangedBroadcast(intents.get(0), WIFI_STATE_ENABLING, + WIFI_STATE_DISABLED); + checkWifiConnectModeStateChangedBroadcast(intents.get(1), WIFI_STATE_DISABLED, + WIFI_STATE_UNKNOWN); + verify(mListener).onStartFailure(); } /** @@ -209,7 +293,7 @@ public class ClientModeManagerTest { */ @Test public void clientModeStartCalledTwice() throws Exception { - startClientModeAndVerifyEnabled(); + startClientInConnectModeAndVerifyEnabled(); reset(mWifiNative, mContext); mClientModeManager.start(); mLooper.dispatchAll(); @@ -221,12 +305,13 @@ public class ClientModeManagerTest { */ @Test public void clientModeStopCleansUpState() throws Exception { - startClientModeAndVerifyEnabled(); + startClientInConnectModeAndVerifyEnabled(); reset(mContext, mListener); mClientModeManager.stop(); mLooper.dispatchAll(); + verify(mListener).onStopped(); - verifyNotificationsForCleanShutdown(WIFI_STATE_ENABLED); + verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED); // on an explicit stop, we should not trigger the callback verifyNoMoreInteractions(mListener); @@ -237,18 +322,18 @@ public class ClientModeManagerTest { */ @Test public void clientModeStopWhenNotStartedDoesNotUpdateScanStateUpdates() throws Exception { - startClientModeAndVerifyEnabled(); + startClientInConnectModeAndVerifyEnabled(); reset(mContext); mClientModeManager.stop(); mLooper.dispatchAll(); - verifyNotificationsForCleanShutdown(WIFI_STATE_ENABLED); + verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED); reset(mContext, mListener); // now call stop again mClientModeManager.stop(); mLooper.dispatchAll(); verify(mContext, never()).sendStickyBroadcastAsUser(any(), any()); - verify(mListener, never()).onStateChanged(anyInt()); + verifyNoMoreInteractions(mListener); } /** @@ -256,13 +341,14 @@ public class ClientModeManagerTest { */ @Test public void clientModeStartedStopsWhenInterfaceDown() throws Exception { - startClientModeAndVerifyEnabled(); + startClientInConnectModeAndVerifyEnabled(); reset(mContext); when(mClientModeImpl.isConnectedMacRandomizationEnabled()).thenReturn(false); mInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME); mLooper.dispatchAll(); verify(mClientModeImpl).failureDetected(eq(SelfRecovery.REASON_STA_IFACE_DOWN)); - verifyNotificationsForFailure(); + verifyConnectModeNotificationsForFailure(); + verify(mListener).onStopped(); } /** @@ -272,7 +358,7 @@ public class ClientModeManagerTest { @Test public void clientModeStartedWithConnectedMacRandDoesNotStopWhenInterfaceDown() throws Exception { - startClientModeAndVerifyEnabled(); + startClientInConnectModeAndVerifyEnabled(); reset(mContext); when(mClientModeImpl.isConnectedMacRandomizationEnabled()).thenReturn(true); mInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME); @@ -286,12 +372,13 @@ public class ClientModeManagerTest { */ @Test public void clientModeStartedStopsOnInterfaceDestroyed() throws Exception { - startClientModeAndVerifyEnabled(); + startClientInConnectModeAndVerifyEnabled(); reset(mContext); mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME); mLooper.dispatchAll(); - verifyNotificationsForCleanShutdown(WIFI_STATE_ENABLED); + verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED); verify(mClientModeImpl).handleIfaceDestroyed(); + verify(mListener).onStopped(); } /** @@ -299,7 +386,7 @@ public class ClientModeManagerTest { */ @Test public void noCallbackOnInterfaceDestroyedWhenAlreadyStopped() throws Exception { - startClientModeAndVerifyEnabled(); + startClientInConnectModeAndVerifyEnabled(); reset(mListener); @@ -309,8 +396,37 @@ public class ClientModeManagerTest { // now trigger interface destroyed and make sure callback doesn't get called mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME); mLooper.dispatchAll(); + verify(mListener).onStopped(); verifyNoMoreInteractions(mListener); verify(mClientModeImpl, never()).handleIfaceDestroyed(); } + + /** + * Entering ScanOnly state starts the WakeupController. + */ + @Test + public void scanModeEnterStartsWakeupController() throws Exception { + startClientInScanOnlyModeAndVerifyEnabled(); + + verify(mWakeupController).start(); + } + + /** + * Exiting ScanOnly state stops the WakeupController. + */ + @Test + public void scanModeExitStopsWakeupController() throws Exception { + startClientInScanOnlyModeAndVerifyEnabled(); + + mClientModeManager.stop(); + mLooper.dispatchAll(); + + InOrder inOrder = inOrder(mWakeupController, mWifiNative, mListener); + + inOrder.verify(mListener).onStarted(); + inOrder.verify(mWakeupController).start(); + inOrder.verify(mWakeupController).stop(); + inOrder.verify(mWifiNative).teardownInterface(eq(TEST_INTERFACE_NAME)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java b/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java index 3f3bbe6e72..871218f1f1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java @@ -30,6 +30,7 @@ public final class ConcreteCandidate implements WifiCandidates.Candidate { private boolean mIsPasspoint; private boolean mIsEphemeral; private boolean mIsTrusted; + private boolean mIsMetered; private int mEvaluatorId = -1; private int mEvaluatorScore = Integer.MIN_VALUE; private double mLastSelectionWeight; @@ -51,6 +52,7 @@ public final class ConcreteCandidate implements WifiCandidates.Candidate { mIsPasspoint = candidate.isPasspoint(); mIsEphemeral = candidate.isEphemeral(); mIsTrusted = candidate.isTrusted(); + mIsMetered = candidate.isMetered(); mEvaluatorId = candidate.getEvaluatorId(); mEvaluatorScore = candidate.getEvaluatorScore(); mLastSelectionWeight = candidate.getLastSelectionWeight(); @@ -134,6 +136,16 @@ public final class ConcreteCandidate implements WifiCandidates.Candidate { return mIsTrusted; } + public ConcreteCandidate setMetered(boolean isMetered) { + mIsMetered = isMetered; + return this; + } + + @Override + public boolean isMetered() { + return mIsMetered; + } + public ConcreteCandidate setEvaluatorId(int evaluatorId) { mEvaluatorId = evaluatorId; return this; diff --git a/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java b/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java index 97141c4963..0b3cc45e88 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java @@ -19,10 +19,9 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.when; -import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.content.pm.UserInfo; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; @@ -48,7 +47,8 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.ConfigurationMapTest}. */ @SmallTest -public class ConfigurationMapTest { +public class ConfigurationMapTest extends WifiBaseTest { + private static final int SYSTEM_MANAGE_PROFILE_USER_ID = 12; private static final List<WifiConfiguration> CONFIGS = Arrays.asList( WifiConfigurationTestUtil.generateWifiConfig( 0, 1000000, "\"red\"", true, true, null, null), @@ -67,7 +67,7 @@ public class ConfigurationMapTest { static { USER_PROFILES.put(UserHandle.USER_SYSTEM, Arrays.asList( new UserInfo(UserHandle.USER_SYSTEM, "Owner", 0), - new UserInfo(12, "Managed Profile", 0))); + new UserInfo(SYSTEM_MANAGE_PROFILE_USER_ID, "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))); } @@ -84,12 +84,15 @@ public class ConfigurationMapTest { public void setUp() { MockitoAnnotations.initMocks(this); - when(mUserManager.getProfiles(anyInt())) - .then(new AnswerWithArguments() { - public List<UserInfo> answer(int userId) { - return USER_PROFILES.get(userId); - } - }); + // by default, return false + when(mUserManager.isSameProfileGroup(any(), any())).thenReturn(false); + // return true for these 2 userids + when(mUserManager.isSameProfileGroup(UserHandle.SYSTEM, + UserHandle.of(SYSTEM_MANAGE_PROFILE_USER_ID))) + .thenReturn(true); + when(mUserManager.isSameProfileGroup(UserHandle.of(SYSTEM_MANAGE_PROFILE_USER_ID), + UserHandle.SYSTEM)) + .thenReturn(true); mConfigs = new ConfigurationMap(mUserManager); } @@ -133,8 +136,10 @@ public class ConfigurationMapTest { // user. Also, check that *ForAllUsers() methods can be used to access all network // configurations, irrespective of their visibility to the current user. for (WifiConfiguration config : configs) { - if (WifiConfigurationUtil.isVisibleToAnyProfile(config, - USER_PROFILES.get(mCurrentUserId))) { + final UserHandle currentUser = UserHandle.of(mCurrentUserId); + final UserHandle creatorUser = UserHandle.getUserHandleForUid(config.creatorUid); + if (config.shared || currentUser.equals(creatorUser) + || mUserManager.isSameProfileGroup(currentUser, creatorUser)) { configsForCurrentUser.add(config); if (config.status != WifiConfiguration.Status.DISABLED) { enabledConfigsForCurrentUser.add(config); @@ -228,7 +233,7 @@ public class ConfigurationMapTest { final WifiConfiguration config1 = CONFIGS.get(0); // Verify that there are no network configurations to start with. - switchUser(UserHandle.getUserId(config1.creatorUid)); + switchUser(UserHandle.getUserHandleForUid(config1.creatorUid).getIdentifier()); verifyGetters(configs); // Add |config1|. diff --git a/service/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java index 702aa99dff..9829427e36 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java @@ -42,7 +42,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.DeletedEphemeralSsidsStoreData}. */ @SmallTest -public class DeletedEphemeralSsidsStoreDataTest { +public class DeletedEphemeralSsidsStoreDataTest extends WifiBaseTest { private static final String TEST_SSID1 = "SSID 1"; private static final String TEST_SSID2 = "SSID 2"; private static final long TEST_SSID1_TSTAMP = 6837367L; diff --git a/service/tests/wifitests/src/com/android/server/wifi/DeviceConfigFacadeTest.java b/service/tests/wifitests/src/com/android/server/wifi/DeviceConfigFacadeTest.java new file mode 100644 index 0000000000..1c04b7439f --- /dev/null +++ b/service/tests/wifitests/src/com/android/server/wifi/DeviceConfigFacadeTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.*; + +import android.app.test.MockAnswerUtil.AnswerWithArguments; +import android.content.Context; +import android.os.Handler; +import android.os.test.TestLooper; +import android.provider.DeviceConfig; +import android.provider.DeviceConfig.OnPropertiesChangedListener; + +import androidx.test.filters.SmallTest; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.R; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; + + +/** + * Unit tests for {@link com.android.server.wifi.DeviceConfigFacade}. + */ +@SmallTest +public class DeviceConfigFacadeTest extends WifiBaseTest { + @Mock Context mContext; + @Mock WifiMetrics mWifiMetrics; + + final ArgumentCaptor<OnPropertiesChangedListener> mOnPropertiesChangedListenerCaptor = + ArgumentCaptor.forClass(OnPropertiesChangedListener.class); + + private DeviceConfigFacade mDeviceConfigFacade; + private TestLooper mLooper = new TestLooper(); + private MockResources mResources; + private MockitoSession mSession; + + /** + * Setup the mocks and an instance of WifiConfigManager before each test. + */ + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mResources = new MockResources(); + mResources.setBoolean( + R.bool.config_wifi_aggressive_randomization_ssid_whitelist_enabled, false); + when(mContext.getResources()).thenReturn(mResources); + + // static mocking + mSession = ExtendedMockito.mockitoSession() + .mockStatic(DeviceConfig.class, withSettings().lenient()) + .startMocking(); + // Have DeviceConfig return the default value passed in. + when(DeviceConfig.getBoolean(anyString(), anyString(), anyBoolean())) + .then(new AnswerWithArguments() { + public boolean answer(String namespace, String field, boolean def) { + return def; + } + }); + when(DeviceConfig.getInt(anyString(), anyString(), anyInt())) + .then(new AnswerWithArguments() { + public int answer(String namespace, String field, int def) { + return def; + } + }); + + mDeviceConfigFacade = new DeviceConfigFacade(mContext, new Handler(mLooper.getLooper()), + mWifiMetrics); + verify(() -> DeviceConfig.addOnPropertiesChangedListener(anyString(), any(), + mOnPropertiesChangedListenerCaptor.capture())); + } + + /** + * Called after each test + */ + @After + public void cleanup() { + validateMockitoUsage(); + mSession.finishMocking(); + } + + /** + * Verifies that all fields are updated properly. + */ + @Test + public void testFieldUpdates() throws Exception { + // First verify fields are set to their default values. + assertEquals(false, mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled()); + assertEquals(DeviceConfigFacade.DEFAULT_ABNORMAL_CONNECTION_DURATION_MS, + mDeviceConfigFacade.getAbnormalConnectionDurationMs()); + assertEquals(false, + mDeviceConfigFacade.isAggressiveMacRandomizationSsidWhitelistEnabled()); + assertEquals(DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS, + mDeviceConfigFacade.getDataStallDurationMs()); + assertEquals(DeviceConfigFacade.DEFAULT_DATA_STALL_TX_TPUT_THR_KBPS, + mDeviceConfigFacade.getDataStallTxTputThrKbps()); + assertEquals(DeviceConfigFacade.DEFAULT_DATA_STALL_RX_TPUT_THR_KBPS, + mDeviceConfigFacade.getDataStallRxTputThrKbps()); + assertEquals(DeviceConfigFacade.DEFAULT_DATA_STALL_TX_PER_THR, + mDeviceConfigFacade.getDataStallTxPerThr()); + assertEquals(DeviceConfigFacade.DEFAULT_DATA_STALL_CCA_LEVEL_THR, + mDeviceConfigFacade.getDataStallCcaLevelThr()); + + // Simulate updating the fields + when(DeviceConfig.getBoolean(anyString(), eq("abnormal_connection_bugreport_enabled"), + anyBoolean())).thenReturn(true); + when(DeviceConfig.getInt(anyString(), eq("abnormal_connection_duration_ms"), + anyInt())).thenReturn(100); + when(DeviceConfig.getBoolean(anyString(), + eq("aggressive_randomization_ssid_whitelist_enabled"), + anyBoolean())).thenReturn(true); + when(DeviceConfig.getInt(anyString(), eq("data_stall_duration_ms"), + anyInt())).thenReturn(0); + when(DeviceConfig.getInt(anyString(), eq("data_stall_tx_tput_thr_kbps"), + anyInt())).thenReturn(1000); + when(DeviceConfig.getInt(anyString(), eq("data_stall_rx_tput_thr_kbps"), + anyInt())).thenReturn(1500); + when(DeviceConfig.getInt(anyString(), eq("data_stall_tx_per_thr"), + anyInt())).thenReturn(95); + when(DeviceConfig.getInt(anyString(), eq("data_stall_cca_level_thr"), + anyInt())).thenReturn(80); + mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null); + + // Verifying fields are updated to the new values + assertEquals(true, mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled()); + assertEquals(100, mDeviceConfigFacade.getAbnormalConnectionDurationMs()); + assertEquals(true, mDeviceConfigFacade.isAggressiveMacRandomizationSsidWhitelistEnabled()); + assertEquals(0, mDeviceConfigFacade.getDataStallDurationMs()); + assertEquals(1000, mDeviceConfigFacade.getDataStallTxTputThrKbps()); + assertEquals(1500, mDeviceConfigFacade.getDataStallRxTputThrKbps()); + assertEquals(95, mDeviceConfigFacade.getDataStallTxPerThr()); + assertEquals(80, mDeviceConfigFacade.getDataStallCcaLevelThr()); + } +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java index ee1264a9e4..9aaaca28a2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java @@ -54,6 +54,7 @@ import android.content.Context; import android.net.wifi.EasyConnectStatusCallback; import android.net.wifi.IDppCallback; import android.net.wifi.WifiConfiguration; +import android.os.Handler; import android.os.IBinder; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; @@ -70,7 +71,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link DppManager}. */ @SmallTest -public class DppManagerTest { +public class DppManagerTest extends WifiBaseTest { private static final String TAG = "DppManagerTest"; private static final String TEST_INTERFACE_NAME = "testif0"; private static final int TEST_PEER_ID = 1; @@ -133,8 +134,8 @@ public class DppManagerTest { } private DppManager createDppManager() { - DppManager dppManger = new DppManager(mLooper.getLooper(), mWifiNative, mWifiConfigManager, - mContext, mDppMetrics); + DppManager dppManger = new DppManager(new Handler(mLooper.getLooper()), mWifiNative, + mWifiConfigManager, mContext, mDppMetrics); dppManger.mDppTimeoutMessage = mWakeupMessage; dppManger.enableVerboseLogging(1); return dppManger; diff --git a/service/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java index e3b980256e..303c6e7d7b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java @@ -47,7 +47,7 @@ import java.util.Random; * Unit tests for {@link com.android.server.wifi.DppMetrics}. */ @SmallTest -public class DppMetricsTest { +public class DppMetricsTest extends WifiBaseTest { private static final int MAX_ITERATIONS = 30; private DppMetrics mDppMetrics = new DppMetrics(); diff --git a/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java index 36da41bd3a..2e68864936 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java @@ -85,7 +85,7 @@ import java.util.Set; * Unit test harness for HalDeviceManagerTest. */ @SmallTest -public class HalDeviceManagerTest { +public class HalDeviceManagerTest extends WifiBaseTest { private HalDeviceManager mDut; @Mock IServiceManager mServiceManagerMock; @Mock IWifi mWifiMock; @@ -107,7 +107,7 @@ public class HalDeviceManagerTest { private class HalDeviceManagerSpy extends HalDeviceManager { HalDeviceManagerSpy() { - super(mClock, mTestLooper.getLooper()); + super(mClock, mHandler); } @Override diff --git a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java index 2cf8f222fd..e2a32272f7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java @@ -31,6 +31,7 @@ import android.hardware.wifi.hostapd.V1_1.IHostapdCallback; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; import android.net.wifi.WifiConfiguration; +import android.os.Handler; import android.os.IHwBinder; import android.os.RemoteException; import android.os.test.TestLooper; @@ -53,7 +54,7 @@ import java.util.ArrayList; * Unit tests for HostapdHal */ @SmallTest -public class HostapdHalTest { +public class HostapdHalTest extends WifiBaseTest { private static final String IFACE_NAME = "mock-wlan0"; private static final String NETWORK_SSID = "test-ssid"; private static final String NETWORK_PSK = "test-psk"; @@ -88,7 +89,7 @@ public class HostapdHalTest { private class HostapdHalSpy extends HostapdHal { HostapdHalSpy() { - super(mContext, mLooper.getLooper()); + super(mContext, new Handler(mLooper.getLooper())); } @Override diff --git a/service/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java b/service/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java index c87f2b3f98..522dba32e6 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java @@ -31,7 +31,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.IMSIParameter}. */ @SmallTest -public class IMSIParameterTest { +public class IMSIParameterTest extends WifiBaseTest { /** * Data points for testing function {@link IMSIParameter#build}. */ diff --git a/service/tests/wifitests/src/com/android/server/wifi/LastMileLoggerTest.java b/service/tests/wifitests/src/com/android/server/wifi/LastMileLoggerTest.java index 0180d6a04d..6d76f4a858 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/LastMileLoggerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/LastMileLoggerTest.java @@ -24,10 +24,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; -import android.os.FileUtils; - import androidx.test.filters.SmallTest; +import com.android.server.wifi.util.FileUtils; + import libcore.io.IoUtils; import org.junit.Before; @@ -44,7 +44,7 @@ import java.io.StringWriter; * Unit tests for {@link LastMileLogger}. */ @SmallTest -public class LastMileLoggerTest { +public class LastMileLoggerTest extends WifiBaseTest { @Mock WifiInjector mWifiInjector; @Spy FakeWifiLog mLog; @@ -58,7 +58,7 @@ public class LastMileLoggerTest { mTraceDataFile.deleteOnExit(); mTraceEnableFile.deleteOnExit(); mTraceReleaseFile.deleteOnExit(); - FileUtils.stringToFile(mTraceEnableFile, "0"); + FileUtils.stringToFile(mTraceEnableFile.getPath(), "0"); mLastMileLogger = new LastMileLogger(mWifiInjector, mTraceDataFile.getPath(), mTraceEnableFile.getPath(), mTraceReleaseFile.getPath()); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java index 32eb78d8cd..91692be323 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/LinkProbeManagerTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.database.ContentObserver; import android.net.wifi.WifiInfo; +import android.os.Handler; import android.os.test.TestLooper; import android.provider.Settings; @@ -50,7 +51,7 @@ import java.util.HashSet; * Unit tests for LinkProbeManager */ @SmallTest -public class LinkProbeManagerTest { +public class LinkProbeManagerTest extends WifiBaseTest { private static final String TEST_IFACE_NAME = "testIfaceName"; private static final String TEST_BSSID = "6c:f3:7f:ae:8c:f3"; @@ -98,7 +99,7 @@ public class LinkProbeManagerTest { private void initLinkProbeManager() { mLinkProbeManager = new LinkProbeManager(mClock, mWifiNative, mWifiMetrics, - mFrameworkFacade, mLooper.getLooper(), mContext); + mFrameworkFacade, new Handler(mLooper.getLooper()), mContext); } /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java index e0d2b5fdd8..b90332ad26 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java @@ -19,15 +19,12 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; +import android.net.wifi.ILocalOnlyHotspotCallback; 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 androidx.test.filters.SmallTest; @@ -40,14 +37,12 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.LocalOnlyHotspotRequestInfo}. */ @SmallTest -public class LocalOnlyHotspotRequestInfoTest { +public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest { private static final String TAG = "LocalOnlyHotspotRequestInfoTest"; @Mock IBinder mAppBinder; - @Mock LocalOnlyHotspotRequestInfo.RequestingApplicationDeathCallback mCallback; - private Handler mHandler; - private Messenger mMessenger; - private TestLooper mTestLooper; + @Mock ILocalOnlyHotspotCallback mCallback; + @Mock LocalOnlyHotspotRequestInfo.RequestingApplicationDeathCallback mDeathCallback; RemoteException mRemoteException; private LocalOnlyHotspotRequestInfo mLOHSRequestInfo; @@ -57,9 +52,7 @@ public class LocalOnlyHotspotRequestInfoTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTestLooper = new TestLooper(); - mHandler = new Handler(mTestLooper.getLooper()); - mMessenger = new Messenger(mHandler); + when(mCallback.asBinder()).thenReturn(mAppBinder); mRemoteException = new RemoteException("Test Remote Exception"); } @@ -69,32 +62,24 @@ public class LocalOnlyHotspotRequestInfoTest { */ @Test public void verifyBinderLinkToDeathIsCalled() throws Exception { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); verify(mAppBinder).linkToDeath(eq(mLOHSRequestInfo), eq(0)); } /** - * Calls to create the requestor to binder death should not pass null callback. + * Calls to link the requestor to binder death should not pass null binder */ @Test(expected = NullPointerException.class) public void verifyNullCallbackChecked() throws Exception { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, null); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(null, mDeathCallback); } /** - * 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 + * Calls to create the requestor to binder death should not pass null death callback. */ @Test(expected = NullPointerException.class) - public void verifyNullBinderChecked() throws Exception { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(null, mMessenger, mCallback); + public void verifyNullDeathCallbackChecked() throws Exception { + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, null); } /** @@ -102,7 +87,7 @@ public class LocalOnlyHotspotRequestInfoTest { */ @Test public void verifyUnlinkDeathRecipientUnlinksFromBinder() throws Exception { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); mLOHSRequestInfo.unlinkDeathRecipient(); verify(mAppBinder).unlinkToDeath(eq(mLOHSRequestInfo), eq(0)); } @@ -112,9 +97,9 @@ public class LocalOnlyHotspotRequestInfoTest { */ @Test public void verifyBinderDeathTriggersCallback() { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); mLOHSRequestInfo.binderDied(); - verify(mCallback).onLocalOnlyHotspotRequestorDeath(eq(mLOHSRequestInfo)); + verify(mDeathCallback).onLocalOnlyHotspotRequestorDeath(eq(mLOHSRequestInfo)); } /** @@ -124,8 +109,8 @@ public class LocalOnlyHotspotRequestInfoTest { 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)); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); + verify(mDeathCallback).onLocalOnlyHotspotRequestorDeath(eq(mLOHSRequestInfo)); } /** @@ -133,7 +118,7 @@ public class LocalOnlyHotspotRequestInfoTest { */ @Test public void verifyPid() { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); assertEquals(Process.myPid(), mLOHSRequestInfo.getPid()); } @@ -142,12 +127,10 @@ public class LocalOnlyHotspotRequestInfoTest { */ @Test public void verifySendFailedMessage() throws Exception { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); mLOHSRequestInfo.sendHotspotFailedMessage( WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC); - Message message = mTestLooper.nextMessage(); - assertEquals(WifiManager.HOTSPOT_FAILED, message.what); - assertEquals(WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC, message.arg1); + verify(mCallback).onHotspotFailed(WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC); } /** @@ -155,12 +138,10 @@ public class LocalOnlyHotspotRequestInfoTest { */ @Test public void verifySendStartedMessage() throws Exception { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); WifiConfiguration config = mock(WifiConfiguration.class); mLOHSRequestInfo.sendHotspotStartedMessage(config); - Message message = mTestLooper.nextMessage(); - assertEquals(WifiManager.HOTSPOT_STARTED, message.what); - assertEquals(config, (WifiConfiguration) message.obj); + verify(mCallback).onHotspotStarted(config); } /** @@ -168,9 +149,8 @@ public class LocalOnlyHotspotRequestInfoTest { */ @Test public void verifySendStoppedMessage() throws Exception { - mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback); mLOHSRequestInfo.sendHotspotStoppedMessage(); - Message message = mTestLooper.nextMessage(); - assertEquals(WifiManager.HOTSPOT_STOPPED, message.what); + verify(mCallback).onHotspotStopped(); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/LogcatLogTest.java b/service/tests/wifitests/src/com/android/server/wifi/LogcatLogTest.java index e75404e768..a29c863d8e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/LogcatLogTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/LogcatLogTest.java @@ -29,7 +29,7 @@ import org.junit.Test; * Unit tests for {@link LogcatLog}. */ @SmallTest -public class LogcatLogTest { +public class LogcatLogTest extends WifiBaseTest { private static final String TAG = "LogcatLogTest"; private LogcatLog mLogger; diff --git a/service/tests/wifitests/src/com/android/server/wifi/MemoryStoreImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/MemoryStoreImplTest.java index a4ac1e8c3f..0288dd50a5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/MemoryStoreImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/MemoryStoreImplTest.java @@ -37,7 +37,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.MemoryStoreImpl}. */ @SmallTest -public class MemoryStoreImplTest { +public class MemoryStoreImplTest extends WifiBaseTest { @Mock Context mContext; @Mock WifiScoreCard mWifiScoreCard; @Mock WifiScoreCard.BlobListener mBlobListener; diff --git a/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java index 7336c41197..6032680b63 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java @@ -52,7 +52,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.NetworkListStoreData}. */ @SmallTest -public class NetworkListStoreDataTest { +public class NetworkListStoreDataTest extends WifiBaseTest { private static final String TEST_SSID = "WifiConfigStoreDataSSID_"; private static final String TEST_CONNECT_CHOICE = "XmlUtilConnectChoice"; @@ -68,6 +68,7 @@ public class NetworkListStoreDataTest { + "<string name=\"SSID\">%s</string>\n" + "<null name=\"BSSID\" />\n" + "<null name=\"PreSharedKey\" />\n" + + "<null name=\"SaePasswordId\" />\n" + "<null name=\"WEPKeys\" />\n" + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" @@ -123,6 +124,7 @@ public class NetworkListStoreDataTest { + "<string name=\"SSID\">%s</string>\n" + "<null name=\"BSSID\" />\n" + "<null name=\"PreSharedKey\" />\n" + + "<null name=\"SaePasswordId\" />\n" + "<null name=\"WEPKeys\" />\n" + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" @@ -186,6 +188,7 @@ public class NetworkListStoreDataTest { + "<int name=\"Phase2Method\" value=\"0\" />\n" + "<string name=\"PLMN\"></string>\n" + "<string name=\"Realm\"></string>\n" + + "<int name=\"Ocsp\" value=\"0\" />\n" + "</WifiEnterpriseConfiguration>\n" + "</Network>\n"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java index f40f71bcfc..16f975bc2e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/NetworkRequestStoreDataTest.java @@ -49,7 +49,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.NetworkRequestStoreData}. */ @SmallTest -public class NetworkRequestStoreDataTest { +public class NetworkRequestStoreDataTest extends WifiBaseTest { private static final String TEST_PACKAGE_NAME_1 = "com.android.test.1"; private static final String TEST_PACKAGE_NAME_2 = "com.android.test.2"; private static final String TEST_CORRUPT_DATA_INVALID_SSID = diff --git a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java index fadfc6bc53..d301d6b959 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java @@ -50,7 +50,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.NetworkSuggestionEvaluator}. */ @SmallTest -public class NetworkSuggestionEvaluatorTest { +public class NetworkSuggestionEvaluatorTest extends WifiBaseTest { private static final int TEST_UID = 3555; private static final int TEST_UID_OTHER = 3545; private static final int TEST_NETWORK_ID = 55; @@ -435,6 +435,10 @@ public class NetworkSuggestionEvaluatorTest { appInteractions, meteredness, priorities, uids, packageNames); // Link the scan result with suggestions. linkScanDetailsWithNetworkSuggestions(scanDetails, suggestions); + // setup config manager interactions. + suggestions[0].wifiConfiguration.fromWifiNetworkSuggestion = true; + suggestions[0].wifiConfiguration.ephemeral = true; + setupAddToWifiConfigManager(suggestions[0].wifiConfiguration); // Existing saved network matching the credentials. when(mWifiConfigManager.getConfiguredNetwork(suggestions[0].wifiConfiguration.configKey())) .thenReturn(suggestions[0].wifiConfiguration); @@ -455,6 +459,9 @@ public class NetworkSuggestionEvaluatorTest { verify(mWifiConfigManager, times(scanSsids.length)) .wasEphemeralNetworkDeleted(anyString()); verify(mWifiConfigManager).getConfiguredNetwork(candidate.configKey()); + verify(mWifiConfigManager).addOrUpdateNetwork(eq(suggestions[0].wifiConfiguration), + eq(suggestions[0].suggestorUid), eq(suggestions[0].suggestorPackageName)); + verify(mWifiConfigManager).getConfiguredNetwork(suggestions[0].wifiConfiguration.networkId); // Verify we did not try to add any new networks or other interactions with // WifiConfigManager. verifyNoMoreInteractions(mWifiConfigManager); @@ -538,6 +545,8 @@ public class NetworkSuggestionEvaluatorTest { // Link the scan result with suggestions. linkScanDetailsWithNetworkSuggestions(scanDetails, suggestions); // setup config manager interactions. + suggestions[0].wifiConfiguration.fromWifiNetworkSuggestion = true; + suggestions[0].wifiConfiguration.ephemeral = true; setupAddToWifiConfigManager(suggestions[0].wifiConfiguration); // Mark the network disabled. suggestions[0].wifiConfiguration.getNetworkSelectionStatus().setNetworkSelectionStatus( @@ -560,6 +569,9 @@ public class NetworkSuggestionEvaluatorTest { .wasEphemeralNetworkDeleted(anyString()); verify(mWifiConfigManager).getConfiguredNetwork(eq( suggestions[0].wifiConfiguration.configKey())); + verify(mWifiConfigManager).addOrUpdateNetwork(eq(suggestions[0].wifiConfiguration), + eq(suggestions[0].suggestorUid), eq(suggestions[0].suggestorPackageName)); + verify(mWifiConfigManager).getConfiguredNetwork(suggestions[0].wifiConfiguration.networkId); verify(mWifiConfigManager).tryEnableNetwork(eq( suggestions[0].wifiConfiguration.networkId)); // Verify we did not try to add any new networks or other interactions with @@ -596,6 +608,8 @@ public class NetworkSuggestionEvaluatorTest { // Link the scan result with suggestions. linkScanDetailsWithNetworkSuggestions(scanDetails, suggestions); // setup config manager interactions. + suggestions[0].wifiConfiguration.fromWifiNetworkSuggestion = true; + suggestions[0].wifiConfiguration.ephemeral = true; setupAddToWifiConfigManager(suggestions[0].wifiConfiguration); // Mark the network disabled. suggestions[0].wifiConfiguration.getNetworkSelectionStatus().setNetworkSelectionStatus( @@ -622,6 +636,9 @@ public class NetworkSuggestionEvaluatorTest { .wasEphemeralNetworkDeleted(anyString()); verify(mWifiConfigManager).getConfiguredNetwork(eq( suggestions[0].wifiConfiguration.configKey())); + verify(mWifiConfigManager).addOrUpdateNetwork(eq(suggestions[0].wifiConfiguration), + eq(suggestions[0].suggestorUid), eq(suggestions[0].suggestorPackageName)); + verify(mWifiConfigManager).getConfiguredNetwork(suggestions[0].wifiConfiguration.networkId); verify(mWifiConfigManager).tryEnableNetwork(eq( suggestions[0].wifiConfiguration.networkId)); // Verify we did not try to add any new networks or other interactions with @@ -727,7 +744,7 @@ public class NetworkSuggestionEvaluatorTest { configs[i].meteredOverride = meteredness[i] ? WifiConfiguration.METERED_OVERRIDE_METERED : WifiConfiguration.METERED_OVERRIDE_NONE; - suggestions[i] = new WifiNetworkSuggestion(configs[i], appInteractions[i], + suggestions[i] = new WifiNetworkSuggestion(configs[i], null, appInteractions[i], false, uids[i], packageNames[i]); } return suggestions; diff --git a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java index 5c1dcb459b..97e20fc953 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java @@ -48,7 +48,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.NetworkSuggestionStoreData}. */ @SmallTest -public class NetworkSuggestionStoreDataTest { +public class NetworkSuggestionStoreDataTest extends WifiBaseTest { private static final int TEST_UID_1 = 14556; private static final int TEST_UID_2 = 14536; private static final String TEST_PACKAGE_NAME_1 = "com.android.test.1"; @@ -156,7 +156,7 @@ public class NetworkSuggestionStoreDataTest { configuration.enterpriseConfig = WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2(); WifiNetworkSuggestion networkSuggestion = - new WifiNetworkSuggestion(configuration, false, false, TEST_UID_1, + new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID_1, TEST_PACKAGE_NAME_1); appInfo.hasUserApproved = false; appInfo.extNetworkSuggestions.add( @@ -186,7 +186,7 @@ public class NetworkSuggestionStoreDataTest { PerAppInfo appInfo1 = new PerAppInfo(TEST_PACKAGE_NAME_1); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_NAME_1); appInfo1.hasUserApproved = false; appInfo1.extNetworkSuggestions.add( @@ -195,7 +195,7 @@ public class NetworkSuggestionStoreDataTest { PerAppInfo appInfo2 = new PerAppInfo(TEST_PACKAGE_NAME_2); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_2, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_2, TEST_PACKAGE_NAME_2); appInfo2.hasUserApproved = true; appInfo2.extNetworkSuggestions.add( @@ -214,10 +214,10 @@ public class NetworkSuggestionStoreDataTest { PerAppInfo appInfo1 = new PerAppInfo(TEST_PACKAGE_NAME_1); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, true, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, true, TEST_UID_1, TEST_PACKAGE_NAME_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_NAME_1); appInfo1.hasUserApproved = true; appInfo1.extNetworkSuggestions.add( @@ -228,10 +228,10 @@ public class NetworkSuggestionStoreDataTest { PerAppInfo appInfo2 = new PerAppInfo(TEST_PACKAGE_NAME_2); WifiNetworkSuggestion networkSuggestion3 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_2, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_2, TEST_PACKAGE_NAME_2); WifiNetworkSuggestion networkSuggestion4 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, true, TEST_UID_2, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, true, TEST_UID_2, TEST_PACKAGE_NAME_2); appInfo2.hasUserApproved = true; appInfo2.extNetworkSuggestions.add( diff --git a/service/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java b/service/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java index 5e1e7283cd..e47a182691 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java @@ -40,9 +40,12 @@ import android.content.Intent; import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; +import android.net.wifi.IActionListener; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; +import android.os.Binder; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -67,7 +70,7 @@ import java.util.List; * Unit tests for {@link OpenNetworkNotifier}. */ @SmallTest -public class OpenNetworkNotifierTest { +public class OpenNetworkNotifierTest extends WifiBaseTest { private static final String TEST_SSID_1 = "Test SSID 1"; private static final String TEST_SSID_2 = "Test SSID 2"; @@ -104,7 +107,7 @@ public class OpenNetworkNotifierTest { when(mFrameworkFacade.getIntegerSetting(mContext, Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, DEFAULT_REPEAT_DELAY_SEC)) .thenReturn(DEFAULT_REPEAT_DELAY_SEC); - when(mContext.getSystemService(Context.USER_SERVICE)) + when(mContext.getSystemService(UserManager.class)) .thenReturn(mUserManager); when(mContext.getResources()).thenReturn(mResources); mDummyNetwork = new ScanResult(); @@ -455,7 +458,9 @@ public class OpenNetworkNotifierTest { /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} disables the feature. */ @Test public void userHasDisallowConfigWifiRestriction_notificationNotDisplayed() { - when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) + // TODO (b/142234604): This will not work on multi-user device scenarios. + when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, + UserHandle.CURRENT_OR_SELF)) .thenReturn(true); mNotificationController.handleScanResults(mOpenNetworks); @@ -474,7 +479,9 @@ public class OpenNetworkNotifierTest { ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); verify(mNotificationManager).notify(anyInt(), any()); - when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) + // TODO (b/142234604): This will not work on multi-user device scenarios. + when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, + UserHandle.CURRENT_OR_SELF)) .thenReturn(true); mNotificationController.handleScanResults(mOpenNetworks); @@ -489,8 +496,8 @@ public class OpenNetworkNotifierTest { @Test public void actionConnectToNetwork_notificationNotShowing_doesNothing() { mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - - verify(mClientModeImpl, never()).sendMessage(any(Message.class)); + verify(mClientModeImpl, never()).connect(any(), anyInt(), any(Binder.class), + any(IActionListener.class), anyInt(), eq(Process.WIFI_UID)); } /** @@ -510,7 +517,8 @@ public class OpenNetworkNotifierTest { mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK)); - verify(mClientModeImpl).sendMessage(any(Message.class)); + verify(mClientModeImpl).connect(eq(null), eq(TEST_NETWORK_ID), any(Binder.class), + any(IActionListener.class), anyInt(), eq(Process.WIFI_UID)); // Connecting Notification verify(mNotificationBuilder).createNetworkConnectingNotification(OPEN_NET_NOTIFIER_TAG, mDummyNetwork); @@ -688,9 +696,11 @@ public class OpenNetworkNotifierTest { verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID, WifiMetricsProto.ConnectionEvent.NOMINATOR_OPEN_NETWORK_AVAILABLE); - ArgumentCaptor<Message> connectMessageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(connectMessageCaptor.capture()); - Message connectMessage = connectMessageCaptor.getValue(); + ArgumentCaptor<IActionListener> connectListenerCaptor = + ArgumentCaptor.forClass(IActionListener.class); + verify(mClientModeImpl).connect(eq(null), eq(TEST_NETWORK_ID), any(Binder.class), + connectListenerCaptor.capture(), anyInt(), eq(Process.WIFI_UID)); + IActionListener connectListener = connectListenerCaptor.getValue(); // Connecting Notification verify(mNotificationBuilder).createNetworkConnectingNotification(OPEN_NET_NOTIFIER_TAG, @@ -702,9 +712,7 @@ public class OpenNetworkNotifierTest { ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); verify(mNotificationManager, times(2)).notify(anyInt(), any()); - Message connectFailedMsg = Message.obtain(); - connectFailedMsg.what = WifiManager.CONNECT_NETWORK_FAILED; - connectMessage.replyTo.send(connectFailedMsg); + connectListener.onFailure(WifiManager.ERROR); mLooper.dispatchAll(); // Failed to Connect Notification diff --git a/service/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java index 4df560fd2a..b91dc186b9 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/RandomizedMacStoreDataTest.java @@ -40,7 +40,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.RandomizedMacStoreData}. */ @SmallTest -public class RandomizedMacStoreDataTest { +public class RandomizedMacStoreDataTest extends WifiBaseTest { private static final String TEST_MAC_ADDRESS_1 = "da:a1:19:0:0:0"; private static final String TEST_MAC_ADDRESS_2 = "ff:ff:ff:0:0:0"; private static final String TEST_CONFIG_KEY_1 = "TP-LINK_B6C1_5GWPA_PSK"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/RttManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/RttManagerTest.java index e179142fc6..1cb8eac735 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/RttManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/RttManagerTest.java @@ -31,7 +31,7 @@ import org.junit.Test; * Unit test for {@link RttManager} */ @SmallTest -public class RttManagerTest { +public class RttManagerTest extends WifiBaseTest { // Verify ParcelableRttParams are the same after writing and reading from parcel. private void verifyReadWriteParcelForRttParams(ParcelableRttParams params) { diff --git a/service/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java index 23edbd7f18..72f48ffa46 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java @@ -29,7 +29,7 @@ import org.junit.Test; * unit tests for {@link com.android.server.wifi.SarInfo}. */ @SmallTest -public class SarInfoTest { +public class SarInfoTest extends WifiBaseTest { private static final String TAG = "WifiSarInfoTest"; private SarInfo mSarInfo; diff --git a/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java index e8d0820018..39f4d6a0ee 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java @@ -59,7 +59,7 @@ import java.util.List; * unit tests for {@link com.android.server.wifi.SarManager}. */ @SmallTest -public class SarManagerTest { +public class SarManagerTest extends WifiBaseTest { private static final String TAG = "WifiSarManagerTest"; private static final String OP_PACKAGE_NAME = "com.xxx"; private static final String SAR_SENSOR_NAME = "com.google.sensor.sar"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java index ff8c8250a0..19f7422ec7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java @@ -28,9 +28,9 @@ import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.os.SystemClock; import android.telephony.SubscriptionManager; +import android.test.suitebuilder.annotation.SmallTest; import android.util.LocalLog; -import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.server.wifi.WifiNetworkSelector.NetworkEvaluator.OnConnectableListener; @@ -42,13 +42,14 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Collections; import java.util.List; /** * Unit tests for {@link com.android.server.wifi.SavedNetworkEvaluator}. */ @SmallTest -public class SavedNetworkEvaluatorTest { +public class SavedNetworkEvaluatorTest extends WifiBaseTest { /** Sets up test. */ @Before @@ -79,7 +80,8 @@ public class SavedNetworkEvaluatorTest { new ScoringParams(mContext), mWifiConfigManager, mClock, mLocalLog, mWifiConnectivityHelper, mSubscriptionManager); // SIM is absent - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[0]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Collections.emptyList()); } /** Cleans up test. */ @@ -207,6 +209,70 @@ public class SavedNetworkEvaluatorTest { } /** + * Pick a worse candidate that allows auto-join over a better candidate that + * disallows auto-join. + */ + @Test + public void ignoreNetworksIfAutojoinNotAllowed() { + 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(); + + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, mOnConnectableListener); + + ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + + savedConfigs[1].allowAutojoin = false; + candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, mOnConnectableListener); + + chosenScanResult = scanDetails.get(0).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * Do not return a candidate if all networks do not {@link WifiConfiguration#allowAutojoin} + */ + @Test + public void returnNoCandidateIfNoNetworksAllowAutojoin() { + 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.allowAutojoin = false; + } + + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, mOnConnectableListener); + + 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}. */ diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScanOnlyModeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScanOnlyModeManagerTest.java deleted file mode 100644 index c20be33e08..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/ScanOnlyModeManagerTest.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.EXTRA_SCAN_AVAILABLE; -import static android.net.wifi.WifiManager.WIFI_SCAN_AVAILABLE; -import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; -import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; -import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.Intent; -import android.os.UserHandle; -import android.os.test.TestLooper; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Unit tests for {@link ScanOnlyModeManager}. - */ -@SmallTest -public class ScanOnlyModeManagerTest { - private static final String TAG = "ScanOnlyModeManagerTest"; - private static final String TEST_INTERFACE_NAME = "testif0"; - private static final String OTHER_INTERFACE_NAME = "notTestIf"; - - TestLooper mLooper; - - ScanOnlyModeManager mScanOnlyModeManager; - - @Mock Context mContext; - @Mock WifiMetrics mWifiMetrics; - @Mock WifiNative mWifiNative; - @Mock ScanOnlyModeManager.Listener mListener; - @Mock WifiMonitor mWifiMonitor; - @Mock WakeupController mWakeupController; - @Mock SarManager mSarManager; - - final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor = - ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mLooper = new TestLooper(); - - mScanOnlyModeManager = createScanOnlyModeManager(); - mLooper.dispatchAll(); - } - - private ScanOnlyModeManager createScanOnlyModeManager() { - return new ScanOnlyModeManager(mContext, mLooper.getLooper(), mWifiNative, mListener, - mWifiMetrics, mWakeupController, mSarManager); - } - - private void startScanOnlyModeAndVerifyEnabled() throws Exception { - when(mWifiNative.setupInterfaceForClientInScanMode(any())).thenReturn( - TEST_INTERFACE_NAME); - mScanOnlyModeManager.start(); - mLooper.dispatchAll(); - - verify(mWifiNative).setupInterfaceForClientInScanMode( - mInterfaceCallbackCaptor.capture()); - - // now mark the interface as up - mInterfaceCallbackCaptor.getValue().onUp(TEST_INTERFACE_NAME); - mLooper.dispatchAll(); - - checkWifiStateChangeListenerUpdate(WIFI_STATE_ENABLED); - verify(mSarManager).setScanOnlyWifiState(eq(WIFI_STATE_ENABLED)); - } - - private void checkWifiScanStateChangedBroadcast(Intent intent, int expectedCurrentState) { - String action = intent.getAction(); - assertEquals(WIFI_SCAN_AVAILABLE, action); - int currentState = intent.getIntExtra(EXTRA_SCAN_AVAILABLE, WIFI_STATE_UNKNOWN); - assertEquals(expectedCurrentState, currentState); - } - - private void checkWifiStateChangeListenerUpdate(int expectedCurrentState) { - verify(mListener).onStateChanged(eq(expectedCurrentState)); - } - - /** - * ScanMode start sets up an interface in ClientMode for scanning. - */ - @Test - public void scanModeStartAndVerifyEnabled() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - } - - /** - * ScanMode start does not indicate scanning is available when the interface name is empty. - */ - @Test - public void scanModeStartDoesNotSendScanningActiveWhenClientInterfaceNameIsEmpty() - throws Exception { - when(mWifiNative.setupInterfaceForClientInScanMode(any())).thenReturn(""); - mScanOnlyModeManager.start(); - mLooper.dispatchAll(); - - verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL)); - checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN); - } - - /** - * Calling ScanOnlyModeManager.start twice does not crash or restart scan mode. - */ - @Test - public void scanOnlyModeStartCalledTwice() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - reset(mWifiNative, mContext); - mScanOnlyModeManager.start(); - mLooper.dispatchAll(); - verifyNoMoreInteractions(mWifiNative, mContext); - } - - /** - * ScanMode stop properly cleans up state - */ - @Test - public void scanModeStopCleansUpState() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - reset(mContext, mListener); - mScanOnlyModeManager.stop(); - mLooper.dispatchAll(); - verify(mWifiNative).teardownInterface(TEST_INTERFACE_NAME); - verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL)); - verify(mSarManager).setScanOnlyWifiState(eq(WIFI_STATE_DISABLED)); - verifyNoMoreInteractions(mListener); - } - - /** - * ScanMode properly stops when underlying interface is destroyed. - */ - @Test - public void scanModeStopsOnInterfaceDestroyed() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - reset(mContext); - mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME); - mLooper.dispatchAll(); - verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL)); - } - - /** - * Calling stop when ScanMode is not started should not send scan state updates - */ - @Test - public void scanModeStopWhenNotStartedDoesNotUpdateScanStateUpdates() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - mScanOnlyModeManager.stop(); - mLooper.dispatchAll(); - reset(mContext, mListener); - - // now call stop again - mScanOnlyModeManager.stop(); - mLooper.dispatchAll(); - verify(mContext, never()).sendStickyBroadcastAsUser(any(), any()); - verify(mListener, never()).onStateChanged(anyInt()); - } - - /** - * Triggering interface down when ScanOnlyMode is active properly exits the active state and - * reports an error. - */ - @Test - public void scanModeStartedStopsWhenInterfaceDown() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - reset(mContext); - mInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME); - mLooper.dispatchAll(); - verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL)); - checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN); - } - - /** - * Triggering an interface down for a different interface will not exit scan mode. - */ - @Test - public void scanModeStartedDoesNotStopOnDownForDifferentIface() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - reset(mContext, mListener); - mInterfaceCallbackCaptor.getValue().onDown(OTHER_INTERFACE_NAME); - - mLooper.dispatchAll(); - - verifyNoMoreInteractions(mContext, mListener); - } - - /** - * Verify that onDestroyed after scan mode is stopped doesn't trigger a callback. - */ - @Test - public void noCallbackOnInterfaceDestroyedWhenAlreadyStopped() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - - reset(mListener); - - mScanOnlyModeManager.stop(); - mLooper.dispatchAll(); - - // now trigger interface destroyed and make sure callback doesn't get called - mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME); - mLooper.dispatchAll(); - - verifyNoMoreInteractions(mListener); - } - - /** - * Entering StartedState starts the WakeupController. - */ - @Test - public void scanModeEnterStartsWakeupController() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - - verify(mWakeupController).start(); - } - - /** - * Exiting StartedState stops the WakeupController. - */ - @Test - public void scanModeExitStopsWakeupController() throws Exception { - startScanOnlyModeAndVerifyEnabled(); - - mScanOnlyModeManager.stop(); - mLooper.dispatchAll(); - - InOrder inOrder = inOrder(mWakeupController, mWifiNative, mListener); - - inOrder.verify(mWakeupController).start(); - inOrder.verify(mWakeupController).stop(); - inOrder.verify(mWifiNative).teardownInterface(eq(TEST_INTERFACE_NAME)); - } -} diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java index 38e2eaf869..d5286d27a2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java @@ -29,12 +29,16 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; +import android.net.wifi.IScanResultsListener; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; +import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.UserHandle; import android.os.WorkSource; +import android.os.test.TestLooper; import android.provider.Settings; import androidx.test.filters.SmallTest; @@ -56,7 +60,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.ScanRequestProxy}. */ @SmallTest -public class ScanRequestProxyTest { +public class ScanRequestProxyTest extends WifiBaseTest { private static final int TEST_UID = 5; private static final String TEST_PACKAGE_NAME_1 = "com.test.1"; private static final String TEST_PACKAGE_NAME_2 = "com.test.2"; @@ -83,6 +87,10 @@ public class ScanRequestProxyTest { @Mock private Clock mClock; @Mock private FrameworkFacade mFrameworkFacade; @Mock private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; + @Mock private IScanResultsListener mScanResultsListener; + @Mock private IScanResultsListener mAnotherScanResultsListener; + @Mock private TestLooper mLooper; + @Mock private IBinder mBinder; private ArgumentCaptor<WorkSource> mWorkSourceArgumentCaptor = ArgumentCaptor.forClass(WorkSource.class); @@ -132,10 +140,11 @@ public class ScanRequestProxyTest { when(mFrameworkFacade.getIntegerSetting( eq(mContext), eq(Settings.Global.WIFI_SCAN_THROTTLE_ENABLED), anyInt())) .thenReturn(1); + mLooper = new TestLooper(); mScanRequestProxy = new ScanRequestProxy(mContext, mAppOps, mActivityManager, mWifiInjector, mWifiConfigManager, mWifiPermissionsUtil, mWifiMetrics, mClock, - mFrameworkFacade, mock(Handler.class)); + mFrameworkFacade, new Handler(mLooper.getLooper())); } @After @@ -144,6 +153,16 @@ public class ScanRequestProxyTest { validateMockitoUsage(); } + private void enableScanning() { + // Enable scanning + mScanRequestProxy.enableScanning(true, false); + mInOrder.verify(mWifiScanner).registerScanListener(any()); + mInOrder.verify(mWifiScanner).setScanningEnabled(true); + validateScanAvailableBroadcastSent(true); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(782L); + } + /** * Verify scan enable sequence. */ @@ -181,8 +200,8 @@ public class ScanRequestProxyTest { */ @Test public void testStartScanSuccess() { + enableScanning(); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); assertTrue(mWorkSourceArgumentCaptor.getValue().equals( @@ -197,9 +216,9 @@ public class ScanRequestProxyTest { */ @Test public void testStartScanSuccessFromAppWithNetworkSettings() { + enableScanning(); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID)).thenReturn(true); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); assertTrue(mWorkSourceArgumentCaptor.getValue().equals( @@ -212,9 +231,9 @@ public class ScanRequestProxyTest { */ @Test public void testStartScanSuccessFromAppWithNetworkSetupWizard() { + enableScanning(); when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(TEST_UID)).thenReturn(true); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); assertEquals(mWorkSourceArgumentCaptor.getValue(), @@ -310,9 +329,9 @@ public class ScanRequestProxyTest { */ @Test public void testScanSuccessOverwritesPreviousResults() { + enableScanning(); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 1. mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -341,9 +360,9 @@ public class ScanRequestProxyTest { */ @Test public void testScanFailureDoesNotOverwritePreviousResults() { + enableScanning(); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 1. mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -373,9 +392,9 @@ public class ScanRequestProxyTest { */ @Test public void testNewScanRequestAfterPreviousScanSucceeds() { + enableScanning(); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send the scan results for request 1. mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -407,9 +426,9 @@ public class ScanRequestProxyTest { */ @Test public void testNewScanRequestAfterPreviousScanSucceedsWithInvalidScanDatas() { + enableScanning(); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send scan success for request 1, but with invalid scan datas. @@ -441,9 +460,9 @@ public class ScanRequestProxyTest { */ @Test public void testNewScanRequestAfterPreviousScanFailure() { + enableScanning(); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send scan failure for request 1. @@ -504,11 +523,11 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestsDontUseSameListener() { + enableScanning(); WifiScanner.ScanListener listener1; WifiScanner.ScanListener listener2; // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); listener1 = mScanRequestListenerArgumentCaptor.getValue(); @@ -530,14 +549,12 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromSameFgAppThrottled() { + enableScanning(); long firstRequestMs = 782; when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS; i++) { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs + i); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - if (i == 0) { - mInOrder.verify(mWifiScanner).registerScanListener(any()); - } mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); } // Make next scan request from the same package name & ensure that it is throttled. @@ -556,14 +573,12 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromSameFgAppNotThrottled() { + enableScanning(); long firstRequestMs = 782; when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS; i++) { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs + i); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - if (i == 0) { - mInOrder.verify(mWifiScanner).registerScanListener(any()); - } mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); } long lastRequestMs = firstRequestMs + SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS + 1; @@ -582,6 +597,7 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromSameAppWithNetworkSettingsPermissionNotThrottled() { + enableScanning(); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID)).thenReturn(true); long firstRequestMs = 782; @@ -589,9 +605,6 @@ public class ScanRequestProxyTest { for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS; i++) { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs + i); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - if (i == 0) { - mInOrder.verify(mWifiScanner).registerScanListener(any()); - } mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); } // Make next scan request from the same package name & ensure that it is not throttled. @@ -605,6 +618,7 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromSameAppWithNetworkSetupWizardPermissionNotThrottled() { + enableScanning(); when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(TEST_UID)).thenReturn(true); long firstRequestMs = 782; @@ -612,9 +626,6 @@ public class ScanRequestProxyTest { for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS; i++) { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs + i); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - if (i == 0) { - mInOrder.verify(mWifiScanner).registerScanListener(any()); - } mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); } // Make next scan request from the same package name & ensure that it is not throttled. @@ -657,14 +668,12 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromDifferentFgAppsNotThrottled() { + enableScanning(); long firstRequestMs = 782; when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS / 2; i++) { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs + i); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - if (i == 0) { - mInOrder.verify(mWifiScanner).registerScanListener(any()); - } mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); } for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS / 2; i++) { @@ -689,14 +698,12 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromSameAppAfterRemovalAndReinstallNotThrottled() { + enableScanning(); long firstRequestMs = 782; when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS; i++) { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs + i); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - if (i == 0) { - mInOrder.verify(mWifiScanner).registerScanListener(any()); - } mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); } // Now simulate removing the app. @@ -718,14 +725,12 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromSameAppAfterRemovalOnAnotherUserThrottled() { + enableScanning(); long firstRequestMs = 782; when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS; i++) { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs + i); assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - if (i == 0) { - mInOrder.verify(mWifiScanner).registerScanListener(any()); - } mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); } // Now simulate removing the app for another user (User 1). @@ -748,6 +753,7 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromBgAppsThrottled() { + enableScanning(); when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1)) .thenReturn(IMPORTANCE_FOREGROUND_SERVICE + 1); when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2)) @@ -757,7 +763,6 @@ public class ScanRequestProxyTest { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Make scan request 2 from the different package name & ensure that it is throttled. @@ -774,6 +779,7 @@ public class ScanRequestProxyTest { */ @Test public void testSuccessiveScanRequestFromBgAppsNotThrottled() { + enableScanning(); when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1)) .thenReturn(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + 1); when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2)) @@ -783,7 +789,6 @@ public class ScanRequestProxyTest { when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); long secondRequestMs = @@ -802,9 +807,9 @@ public class ScanRequestProxyTest { */ @Test public void testFullInternalScanResultsOverwritesPreviousResults() { + enableScanning(); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 1. mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -832,9 +837,9 @@ public class ScanRequestProxyTest { */ @Test public void testPartialInternalScanResultsDoesNotOverwritePreviousResults() { + enableScanning(); // Make scan request 1. assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); - mInOrder.verify(mWifiScanner).registerScanListener(any()); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 1. mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -949,4 +954,85 @@ public class ScanRequestProxyTest { boolean scanAvailable = scanState == WifiManager.WIFI_STATE_ENABLED; assertEquals(expectedScanAvailable, scanAvailable); } + + /** + * Test register scan result listener from different apps, all of them will receive the event. + */ + @Test + public void testScanSuccessWithMultipleListenerFromDifferentApps() throws Exception { + final int listenerIdentifier1 = 1; + final int listenerIdentifier2 = 2; + Binder binder1 = new Binder(); + Binder binder2 = new Binder(); + mScanRequestProxy.registerScanResultsListener(binder1, mScanResultsListener, + listenerIdentifier1); + mScanRequestProxy.registerScanResultsListener(binder2, mAnotherScanResultsListener, + listenerIdentifier2); + mLooper.dispatchAll(); + testStartScanSuccess(); + // Verify the scan results processing. + mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); + mLooper.dispatchAll(); + verify(mScanResultsListener).onScanResultsAvailable(); + verify(mAnotherScanResultsListener).onScanResultsAvailable(); + validateScanResultsAvailableBroadcastSent(true); + + reset(mScanResultsListener); + reset(mAnotherScanResultsListener); + mScanRequestProxy.unregisterScanResultsListener(listenerIdentifier1); + mLooper.dispatchAll(); + mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); + mLooper.dispatchAll(); + verify(mScanResultsListener, never()).onScanResultsAvailable(); + verify(mAnotherScanResultsListener).onScanResultsAvailable(); + validateScanResultsAvailableBroadcastSent(true); + } + + /** + * Test same app register scan result listener second time will replace the first one. + */ + @Test + public void testScanSuccessWithMultipleListenerFromSameApps() throws Exception { + final int listenerIdentifier = 1; + Binder binder = new Binder(); + mScanRequestProxy.registerScanResultsListener(binder, mScanResultsListener, + listenerIdentifier); + mScanRequestProxy.registerScanResultsListener(binder, mAnotherScanResultsListener, + listenerIdentifier); + testStartScanSuccess(); + // Verify the scan results processing. + mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); + mLooper.dispatchAll(); + verify(mScanResultsListener, never()).onScanResultsAvailable(); + verify(mAnotherScanResultsListener).onScanResultsAvailable(); + validateScanResultsAvailableBroadcastSent(true); + } + + /** + * Test registered scan result listener will be unregistered when calling binder is died. + */ + @Test + public void testUnregisterScanResultListenerOnBinderDied() throws Exception { + final int callbackIdentifier = 1; + ArgumentCaptor<IBinder.DeathRecipient> drCaptor = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + mScanRequestProxy.registerScanResultsListener(mBinder, mScanResultsListener, + callbackIdentifier); + verify(mBinder).linkToDeath(drCaptor.capture(), anyInt()); + testStartScanSuccess(); + // Verify the scan results processing. + mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); + mLooper.dispatchAll(); + verify(mScanResultsListener).onScanResultsAvailable(); + validateScanResultsAvailableBroadcastSent(true); + reset(mScanResultsListener); + drCaptor.getValue().binderDied(); + mLooper.dispatchAll(); + verify(mBinder).unlinkToDeath(drCaptor.capture(), eq(0)); + // Verify the scan results processing. + mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); + mLooper.dispatchAll(); + verify(mScanResultsListener, never()).onScanResultsAvailable(); + validateScanResultsAvailableBroadcastSent(true); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java index 2712ce0771..1cc7fa83fc 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java @@ -30,7 +30,7 @@ import org.junit.Test; * Unit tests for {@link com.android.server.wifi.ScanResultMatchInfoTest}. */ @SmallTest -public class ScanResultMatchInfoTest { +public class ScanResultMatchInfoTest extends WifiBaseTest { /** * Tests that equivalent ScanResultMatchInfo objects are created for WifiConfigurations and * their associated ScanResult diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScanResults.java b/service/tests/wifitests/src/com/android/server/wifi/ScanResults.java index 5800979275..052b4dd2b5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ScanResults.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ScanResults.java @@ -21,6 +21,7 @@ import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiSsid; import com.android.server.wifi.hotspot2.NetworkDetail; +import com.android.server.wifi.util.NativeUtil; import java.math.BigInteger; import java.nio.charset.Charset; @@ -87,6 +88,21 @@ public class ScanResults { return ie; } + public static byte[] generateIERawDatafromScanResultIE(ScanResult.InformationElement[] ies) { + ArrayList<Byte> ieRawData = new ArrayList<>(); + for (int i = 0; i < ies.length; i++) { + if (ies[i].id > 255 || ies[i].bytes.length > 255) { + break; + } + ieRawData.add(BigInteger.valueOf(ies[i].id).toByteArray()[0]); + ieRawData.add(BigInteger.valueOf(ies[i].bytes.length).toByteArray()[0]); + for (int j = 0; j < ies[i].bytes.length; j++) { + ieRawData.add(ies[i].bytes[j]); + } + } + return NativeUtil.byteArrayFromArrayList(ieRawData); + } + /** * Generates an array of random ScanDetails with the given frequencies, seeded by the provided * seed value and test method name and class (annotated with @Test). This method will be @@ -120,7 +136,7 @@ public class ScanResults { bssid, "", rssi, freq, Long.MAX_VALUE, /* needed so that scan results aren't rejected because they are older than scan start */ - ie, anqpLines); + ie, anqpLines, generateIERawDatafromScanResultIE(ie)); results[i] = detail; } return results; diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java index d1de03cbc9..7c60fead09 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java @@ -32,6 +32,7 @@ import android.net.Uri; 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.SystemClock; import android.provider.Settings; @@ -58,7 +59,7 @@ import java.util.List; * Unit tests for {@link ScoredNetworkEvaluator}. */ @SmallTest -public class ScoredNetworkEvaluatorTest { +public class ScoredNetworkEvaluatorTest extends WifiBaseTest { private static final String TEST_PACKAGE_NAME = "name.package.test"; private static final int TEST_UID = 12345; private static final NetworkScorerAppData TEST_APP_DATA = new NetworkScorerAppData( @@ -95,7 +96,7 @@ public class ScoredNetworkEvaluatorTest { ArgumentCaptor.forClass(ContentObserver.class); mScoreCache = new WifiNetworkScoreCache(mContext); mScoredNetworkEvaluator = new ScoredNetworkEvaluator(mContext, - Looper.getMainLooper(), mFrameworkFacade, mNetworkScoreManager, + new Handler(Looper.getMainLooper()), mFrameworkFacade, mNetworkScoreManager, mWifiConfigManager, new LocalLog(0), mScoreCache, mWifiPermissionsUtil); verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(false), observerCaptor.capture()); diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java index e73294589f..a6a3b0f766 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java @@ -48,7 +48,7 @@ import org.mockito.Spy; * Unit tests for {@link com.android.server.wifi.ScoringParams}. */ @SmallTest -public class ScoringParamsTest { +public class ScoringParamsTest extends WifiBaseTest { private static final String EXPECTED_DEFAULTS = "rssi2=-83:-80:-73:-60,rssi5=-80:-77:-70:-57,pps=0:1:100,horizon=15,nud=8,expid=0"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java b/service/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java index bcef92ca92..fe8bfd575f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java @@ -29,45 +29,45 @@ import org.mockito.Mock; * Unit tests for {@link com.android.server.wifi.SelfRecovery}. */ @SmallTest -public class SelfRecoveryTest { +public class SelfRecoveryTest extends WifiBaseTest { SelfRecovery mSelfRecovery; - @Mock WifiController mWifiController; + @Mock ActiveModeWarden mActiveModeWarden; @Mock Clock mClock; @Before public void setUp() throws Exception { initMocks(this); - mSelfRecovery = new SelfRecovery(mWifiController, mClock); + mSelfRecovery = new SelfRecovery(mActiveModeWarden, mClock); } /** * Verifies that invocations of {@link SelfRecovery#trigger(int)} with valid reasons will send - * the restart message to {@link WifiController}. + * the restart message to {@link ActiveModeWarden}. */ @Test public void testValidTriggerReasonsSendMessageToWifiController() { mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + reset(mActiveModeWarden); when(mClock.getElapsedSinceBootMillis()) .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + reset(mActiveModeWarden); } /** * Verifies that invocations of {@link SelfRecovery#trigger(int)} with invalid reasons will not - * send the restart message to {@link WifiController}. + * send the restart message to {@link ActiveModeWarden}. */ @Test public void testInvalidTriggerReasonsDoesNotSendMessageToWifiController() { mSelfRecovery.trigger(-1); - verify(mWifiController, never()).sendMessage(anyInt(), anyString()); + verifyNoMoreInteractions(mActiveModeWarden); mSelfRecovery.trigger(8); - verify(mWifiController, never()).sendMessage(anyInt(), anyString()); + verifyNoMoreInteractions(mActiveModeWarden); } /** @@ -76,7 +76,7 @@ public class SelfRecoveryTest { @Test public void testStaIfaceDownDisablesWifi() { mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); + verify(mActiveModeWarden).recoveryDisableWifi(); } /** @@ -91,58 +91,53 @@ public class SelfRecoveryTest { // aren't ignored for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW / 2; i++) { mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), - anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + reset(mActiveModeWarden); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), - anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + reset(mActiveModeWarden); } if ((SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW % 2) == 1) { mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), - anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + reset(mActiveModeWarden); } // Verify that further attempts to trigger restarts disable wifi mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), - anyString()); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); - reset(mWifiController); + verify(mActiveModeWarden, never()) + .recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + verify(mActiveModeWarden).recoveryDisableWifi(); + reset(mActiveModeWarden); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), - anyString()); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); - reset(mWifiController); + verify(mActiveModeWarden, never()).recoveryRestartWifi(anyInt()); + verify(mActiveModeWarden).recoveryDisableWifi(); + reset(mActiveModeWarden); // Verify L.R.Watchdog can still restart things (It has its own complex limiter) mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), - anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + reset(mActiveModeWarden); // Verify Sta Interface Down will still disable wifi mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); - reset(mWifiController); + verify(mActiveModeWarden).recoveryDisableWifi(); + reset(mActiveModeWarden); // now TRAVEL FORWARDS IN TIME and ensure that more restarts can occur when(mClock.getElapsedSinceBootMillis()) .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1); mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + reset(mActiveModeWarden); when(mClock.getElapsedSinceBootMillis()) .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE); + reset(mActiveModeWarden); } /** @@ -156,9 +151,8 @@ public class SelfRecoveryTest { for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW * 2; i++) { // Verify L.R.Watchdog can still restart things (It has it's own complex limiter) mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), - anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryRestartWifi(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + reset(mActiveModeWarden); } } @@ -172,10 +166,9 @@ public class SelfRecoveryTest { public void testTimeWindowLimiting_staIfaceDown_noEffect() { for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW * 2; i++) { mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); - verify(mWifiController, never()) - .sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); - reset(mWifiController); + verify(mActiveModeWarden).recoveryDisableWifi(); + verify(mActiveModeWarden, never()).recoveryRestartWifi(anyInt()); + reset(mActiveModeWarden); } } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java index c08adc1d1c..1f5166abbe 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java @@ -30,16 +30,31 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; +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.clearInvocations; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; +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.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import android.app.test.TestAlarmManager; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.database.ContentObserver; +import android.net.MacAddress; import android.net.Uri; -import android.net.wifi.IApInterfaceEventCallback; +import android.net.wifi.WifiClient; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.UserHandle; @@ -50,20 +65,25 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.util.WakeupMessage; +import com.android.server.wifi.wificond.IApInterfaceEventCallback; +import com.android.server.wifi.wificond.NativeWifiClient; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; /** Unit tests for {@link SoftApManager}. */ @SmallTest -public class SoftApManagerTest { +public class SoftApManagerTest extends WifiBaseTest { private static final String TAG = "SoftApManagerTest"; @@ -74,6 +94,16 @@ public class SoftApManagerTest { private static final String TEST_INTERFACE_NAME = "testif0"; private static final String OTHER_INTERFACE_NAME = "otherif"; private static final int TEST_NUM_CONNECTED_CLIENTS = 4; + private static final MacAddress TEST_MAC_ADDRESS = MacAddress.fromString("22:33:44:55:66:77"); + private static final WifiClient TEST_CONNECTED_CLIENT = new WifiClient(TEST_MAC_ADDRESS); + private static final List<WifiClient> TEST_CONNECTED_CLIENTS = + new ArrayList(Arrays.asList(TEST_CONNECTED_CLIENT)); + private static final NativeWifiClient TEST_NATIVE_CLIENT = new NativeWifiClient() {{ + macAddress = TEST_MAC_ADDRESS.toByteArray(); + } + }; + private static final List<NativeWifiClient> TEST_CONNECTED_NATIVECLIENTS = + new ArrayList(Arrays.asList(TEST_NATIVE_CLIENT)); private final WifiConfiguration mDefaultApConfig = createDefaultApConfig(); @@ -85,6 +115,7 @@ public class SoftApManagerTest { @Mock Resources mResources; @Mock WifiNative mWifiNative; @Mock WifiManager.SoftApCallback mCallback; + @Mock ActiveModeManager.Listener mListener; @Mock FrameworkFacade mFrameworkFacade; @Mock WifiApConfigStore mWifiApConfigStore; @Mock WifiMetrics mWifiMetrics; @@ -116,6 +147,7 @@ public class SoftApManagerTest { when(mWifiNative.setCountryCodeHal( TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT))) .thenReturn(true); + when(mWifiNative.getFactoryMacAddress(any())).thenReturn(TEST_MAC_ADDRESS); } private WifiConfiguration createDefaultApConfig() { @@ -133,6 +165,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, countryCode, + mListener, mCallback, mWifiApConfigStore, config, @@ -207,6 +240,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, TEST_COUNTRY_CODE, + mListener, mCallback, mWifiApConfigStore, nullApConfig, @@ -218,6 +252,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); + verify(mListener).onStartFailure(); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); @@ -251,6 +286,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, TEST_COUNTRY_CODE, + mListener, mCallback, mWifiApConfigStore, nullApConfig, @@ -262,6 +298,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); + verify(mListener).onStartFailure(); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); @@ -294,6 +331,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, TEST_COUNTRY_CODE, + mListener, mCallback, mWifiApConfigStore, nullApConfig, @@ -305,6 +343,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); + verify(mListener).onStartFailure(); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); @@ -336,6 +375,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, null, + mListener, mCallback, mWifiApConfigStore, softApConfig, @@ -383,6 +423,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, TEST_COUNTRY_CODE, + mListener, mCallback, mWifiApConfigStore, softApConfig, @@ -500,6 +541,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, TEST_COUNTRY_CODE, + mListener, mCallback, mWifiApConfigStore, softApConfig, @@ -539,6 +581,7 @@ public class SoftApManagerTest { mFrameworkFacade, mWifiNative, TEST_COUNTRY_CODE, + mListener, mCallback, mWifiApConfigStore, softApModeConfig, @@ -551,6 +594,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); + verify(mListener).onStartFailure(); verify(mWifiNative).teardownInterface(TEST_INTERFACE_NAME); } @@ -566,6 +610,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); /* Verify no state changes. */ verify(mCallback, never()).onStateChanged(anyInt(), anyInt()); + verifyNoMoreInteractions(mListener); verify(mSarManager, never()).setSapWifiState(anyInt()); verify(mContext, never()).sendStickyBroadcastAsUser(any(), any()); verify(mWifiNative, never()).teardownInterface(anyString()); @@ -583,7 +628,7 @@ public class SoftApManagerTest { // reset to clear verified Intents for ap state change updates reset(mContext); - InOrder order = inOrder(mCallback, mContext); + InOrder order = inOrder(mCallback, mListener, mContext); mSoftApManager.stop(); mLooper.dispatchAll(); @@ -618,7 +663,7 @@ public class SoftApManagerTest { // reset to clear verified Intents for ap state change updates reset(mContext); - InOrder order = inOrder(mCallback, mContext); + InOrder order = inOrder(mCallback, mListener, mContext); mWifiNativeInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME); @@ -637,6 +682,7 @@ public class SoftApManagerTest { checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME, softApModeConfig.getTargetMode()); + order.verify(mListener).onStopped(); } /** @@ -650,6 +696,7 @@ public class SoftApManagerTest { mSoftApManager.stop(); mLooper.dispatchAll(); + verify(mListener).onStopped(); verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0); verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); @@ -660,7 +707,7 @@ public class SoftApManagerTest { mWifiNativeInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME); mLooper.dispatchAll(); - verifyNoMoreInteractions(mCallback); + verifyNoMoreInteractions(mCallback, mListener); } /** @@ -675,7 +722,7 @@ public class SoftApManagerTest { // reset to clear verified Intents for ap state change updates reset(mContext, mCallback, mWifiNative); - InOrder order = inOrder(mCallback, mContext); + InOrder order = inOrder(mCallback, mListener, mContext); mWifiNativeInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME); @@ -683,6 +730,7 @@ public class SoftApManagerTest { order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); + order.verify(mListener).onStopped(); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(3)).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); @@ -717,7 +765,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); - verifyNoMoreInteractions(mContext, mCallback, mWifiNative); + verifyNoMoreInteractions(mContext, mCallback, mListener, mWifiNative); } /** @@ -732,13 +780,14 @@ public class SoftApManagerTest { // reset to clear verified Intents for ap state change updates reset(mContext, mCallback, mWifiNative); - InOrder order = inOrder(mCallback, mContext); + InOrder order = inOrder(mCallback, mListener, mContext); mSoftApListenerCaptor.getValue().onFailure(); mLooper.dispatchAll(); order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); + order.verify(mListener).onStopped(); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(3)).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); @@ -835,17 +884,21 @@ public class SoftApManagerTest { } @Test - public void updatesNumAssociatedStations() throws Exception { + public void updatesConnectedClients() throws Exception { SoftApModeConfiguration apConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); startSoftApAndVerifyEnabled(apConfig); - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged( - TEST_NUM_CONNECTED_CLIENTS); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged( + TEST_CONNECTED_NATIVECLIENTS); mLooper.dispatchAll(); - verify(mCallback).onNumClientsChanged(TEST_NUM_CONNECTED_CLIENTS); - verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(TEST_NUM_CONNECTED_CLIENTS, + verify(mCallback).onConnectedClientsChanged( + Mockito.argThat((List<WifiClient> clients) -> + clients.contains(TEST_CONNECTED_CLIENT)) + ); + verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent( + TEST_CONNECTED_CLIENTS.size(), apConfig.getTargetMode()); } @@ -854,36 +907,41 @@ public class SoftApManagerTest { * trigger callbacks a second time. */ @Test - public void testDoesNotTriggerCallbackForSameNumberClientUpdate() throws Exception { + public void testDoesNotTriggerCallbackForSameClients() throws Exception { SoftApModeConfiguration apConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); startSoftApAndVerifyEnabled(apConfig); - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged( - TEST_NUM_CONNECTED_CLIENTS); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged( + TEST_CONNECTED_NATIVECLIENTS); mLooper.dispatchAll(); // now trigger callback again, but we should have each method only called once - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged( - TEST_NUM_CONNECTED_CLIENTS); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged( + TEST_CONNECTED_NATIVECLIENTS); mLooper.dispatchAll(); - verify(mCallback).onNumClientsChanged(TEST_NUM_CONNECTED_CLIENTS); - verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(TEST_NUM_CONNECTED_CLIENTS, + verify(mCallback).onConnectedClientsChanged( + Mockito.argThat((List<WifiClient> clients) -> + clients.contains(TEST_CONNECTED_CLIENT)) + ); + verify(mWifiMetrics) + .addSoftApNumAssociatedStationsChangedEvent( + TEST_CONNECTED_CLIENTS.size(), apConfig.getTargetMode()); } @Test - public void handlesInvalidNumAssociatedStations() throws Exception { + public void handlesInvalidConnectedClients() throws Exception { SoftApModeConfiguration apConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); startSoftApAndVerifyEnabled(apConfig); /* Invalid values should be ignored */ - final int mInvalidNumClients = -1; - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(mInvalidNumClients); + final List<NativeWifiClient> mInvalidClients = null; + mSoftApListenerCaptor.getValue().onConnectedClientsChanged(mInvalidClients); mLooper.dispatchAll(); - verify(mCallback, never()).onNumClientsChanged(mInvalidNumClients); + verify(mCallback, never()).onConnectedClientsChanged(null); verify(mWifiMetrics, never()).addSoftApNumAssociatedStationsChangedEvent(anyInt(), anyInt()); } @@ -895,20 +953,23 @@ public class SoftApManagerTest { new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); startSoftApAndVerifyEnabled(apConfig); - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged( - TEST_NUM_CONNECTED_CLIENTS); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged( + TEST_CONNECTED_NATIVECLIENTS); mLooper.dispatchAll(); - order.verify(mCallback).onNumClientsChanged(TEST_NUM_CONNECTED_CLIENTS); + verify(mCallback).onConnectedClientsChanged( + Mockito.argThat((List<WifiClient> clients) -> + clients.contains(TEST_CONNECTED_CLIENT)) + ); order.verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent( - TEST_NUM_CONNECTED_CLIENTS, apConfig.getTargetMode()); + TEST_CONNECTED_CLIENTS.size(), apConfig.getTargetMode()); // Verify timer is canceled at this point verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class)); mSoftApManager.stop(); mLooper.dispatchAll(); - order.verify(mCallback).onNumClientsChanged(0); + order.verify(mCallback).onConnectedClientsChanged(new ArrayList<>()); order.verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(0, apConfig.getTargetMode()); // Verify timer is canceled after stop softap @@ -943,8 +1004,8 @@ public class SoftApManagerTest { SoftApModeConfiguration apConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); startSoftApAndVerifyEnabled(apConfig); - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged( - TEST_NUM_CONNECTED_CLIENTS); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged( + TEST_CONNECTED_NATIVECLIENTS); mLooper.dispatchAll(); // Verify timer is canceled @@ -957,16 +1018,23 @@ public class SoftApManagerTest { new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); startSoftApAndVerifyEnabled(apConfig); - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged( - TEST_NUM_CONNECTED_CLIENTS); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged( + TEST_CONNECTED_NATIVECLIENTS); mLooper.dispatchAll(); - verify(mCallback).onNumClientsChanged(TEST_NUM_CONNECTED_CLIENTS); + verify(mCallback).onConnectedClientsChanged( + Mockito.argThat((List<WifiClient> clients) -> + clients.contains(TEST_CONNECTED_CLIENT)) + ); // Verify timer is canceled at this point verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class)); - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0); + List<NativeWifiClient> testClients = new ArrayList(); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged(testClients); mLooper.dispatchAll(); - verify(mCallback, times(2)).onNumClientsChanged(0); + verify(mCallback).onConnectedClientsChanged( + Mockito.argThat((List<WifiClient> clients) -> + clients.contains(TEST_CONNECTED_CLIENT)) + ); // Verify timer is scheduled again verify(mAlarmManager.getAlarmManager(), times(2)).setExact(anyInt(), anyLong(), eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any()); @@ -1042,11 +1110,12 @@ public class SoftApManagerTest { new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); startSoftApAndVerifyEnabled(apConfig); // add some clients - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged( - TEST_NUM_CONNECTED_CLIENTS); + mSoftApListenerCaptor.getValue().onConnectedClientsChanged( + TEST_CONNECTED_NATIVECLIENTS); mLooper.dispatchAll(); // remove all clients - mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0); + mSoftApListenerCaptor.getValue() + .onConnectedClientsChanged(new ArrayList<NativeWifiClient>()); mLooper.dispatchAll(); // Verify timer is not scheduled verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(), @@ -1064,6 +1133,57 @@ public class SoftApManagerTest { verify(mFrameworkFacade).unregisterContentObserver(eq(mContext), eq(mContentObserver)); } + @Test + public void setsRandomMacWhenEnabled() throws Exception { + SoftApModeConfiguration apConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); + when(mResources.getBoolean(R.bool.config_wifi_ap_mac_randomization_supported)) + .thenReturn(true); + ArgumentCaptor<MacAddress> mac = ArgumentCaptor.forClass(MacAddress.class); + when(mWifiNative.setMacAddress(eq(TEST_INTERFACE_NAME), mac.capture())).thenReturn(true); + + startSoftApAndVerifyEnabled(apConfig); + mSoftApManager.stop(); + mLooper.dispatchAll(); + + clearInvocations(mWifiNative, mCallback, mSarManager, mWifiDiagnostics, mWifiMetrics, + mListener, mFrameworkFacade, mContext); + + startSoftApAndVerifyEnabled(apConfig); + mSoftApManager.stop(); + + assertThat(mac.getAllValues()).hasSize(2); + assertThat(mac.getAllValues()).containsNoDuplicates(); + } + + @Test + public void resetsFactoryMacWhenRandomizationOff() throws Exception { + when(mResources.getBoolean(R.bool.config_wifi_ap_mac_randomization_supported)) + .thenReturn(false); + SoftApModeConfiguration apConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); + ArgumentCaptor<MacAddress> mac = ArgumentCaptor.forClass(MacAddress.class); + when(mWifiNative.getFactoryMacAddress(TEST_INTERFACE_NAME)).thenReturn(TEST_MAC_ADDRESS); + when(mWifiNative.setMacAddress(eq(TEST_INTERFACE_NAME), mac.capture())).thenReturn(true); + + startSoftApAndVerifyEnabled(apConfig); + + assertThat(mac.getValue()).isEqualTo(TEST_MAC_ADDRESS); + } + + @Test + public void setMacFailureAllowedWhenRandomizationOff() throws Exception { + when(mResources.getBoolean(R.bool.config_wifi_ap_mac_randomization_supported)) + .thenReturn(false); + SoftApModeConfiguration apConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null); + ArgumentCaptor<MacAddress> mac = ArgumentCaptor.forClass(MacAddress.class); + + when(mWifiNative.setMacAddress(any(), any())).thenReturn(false); + + startSoftApAndVerifyEnabled(apConfig); + } + /** Starts soft AP and verifies that it is enabled successfully. */ protected void startSoftApAndVerifyEnabled( SoftApModeConfiguration softApConfig) throws Exception { @@ -1105,11 +1225,11 @@ public class SoftApManagerTest { mWifiNativeInterfaceCallbackCaptor.getValue().onUp(TEST_INTERFACE_NAME); mLooper.dispatchAll(); order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0); - order.verify(mCallback).onNumClientsChanged(0); + order.verify(mCallback).onConnectedClientsChanged(new ArrayList<>()); verify(mSarManager).setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); verify(mWifiDiagnostics).startLogging(TEST_INTERFACE_NAME); verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(), - eq(UserHandle.ALL)); + eq(UserHandle.ALL)); List<Intent> capturedIntents = intentCaptor.getAllValues(); checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_ENABLING, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME, @@ -1117,6 +1237,7 @@ public class SoftApManagerTest { checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME, softApConfig.getTargetMode()); + verify(mListener).onStarted(); verify(mWifiMetrics).addSoftApUpChangedEvent(true, softApConfig.mTargetMode); verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(true), observerCaptor.capture()); @@ -1124,8 +1245,8 @@ public class SoftApManagerTest { } private void checkApStateChangedBroadcast(Intent intent, int expectedCurrentState, - int expectedPrevState, int expectedErrorCode, - String expectedIfaceName, int expectedMode) { + 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); diff --git a/service/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java index ac6ae21a26..cc6cc689bc 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java @@ -49,7 +49,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.SsidSetStoreData}. */ @SmallTest -public class SsidSetStoreDataTest { +public class SsidSetStoreDataTest extends WifiBaseTest { private static final String TEST_NOTIFIER_NAME = "TestNetwork"; private static final String TEST_SSID1 = "SSID 1"; private static final String TEST_SSID2 = "SSID 2"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java index cae7e2dac6..254f10d0d2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java @@ -24,6 +24,7 @@ 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.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; @@ -38,6 +39,7 @@ 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.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -56,21 +58,24 @@ 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.hardware.wifi.supplicant.V1_3.ConnectionCapabilities; +import android.hardware.wifi.supplicant.V1_3.WifiTechnology; 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.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; +import android.os.Handler; import android.os.IHwBinder; import android.os.RemoteException; import android.os.test.TestLooper; import android.text.TextUtils; -import android.util.SparseArray; import androidx.test.filters.SmallTest; +import com.android.server.wifi.SupplicantStaIfaceHal.PmkCacheStoreData; import com.android.server.wifi.hotspot2.AnqpEvent; import com.android.server.wifi.hotspot2.IconEvent; import com.android.server.wifi.hotspot2.WnmData; @@ -95,7 +100,7 @@ import java.util.Random; * Unit tests for SupplicantStaIfaceHal */ @SmallTest -public class SupplicantStaIfaceHalTest { +public class SupplicantStaIfaceHalTest extends WifiBaseTest { private static final String TAG = "SupplicantStaIfaceHalTest"; private static final Map<Integer, String> NETWORK_ID_TO_SSID = new HashMap<Integer, String>() {{ put(1, "\"ssid1\""); @@ -112,22 +117,27 @@ public class SupplicantStaIfaceHalTest { private static final String ICON_FILE_NAME = "blahblah"; private static final int ICON_FILE_SIZE = 72; private static final String HS20_URL = "http://blahblah"; + private static final long PMK_CACHE_EXPIRATION_IN_SEC = 1024; private @Mock IServiceManager mServiceManagerMock; private @Mock ISupplicant mISupplicantMock; private android.hardware.wifi.supplicant.V1_1.ISupplicant mISupplicantMockV1_1; private android.hardware.wifi.supplicant.V1_2.ISupplicant mISupplicantMockV1_2; + private android.hardware.wifi.supplicant.V1_3.ISupplicant mISupplicantMockV13; private @Mock ISupplicantIface mISupplicantIfaceMock; private @Mock ISupplicantStaIface mISupplicantStaIfaceMock; private @Mock android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface mISupplicantStaIfaceMockV1_1; private @Mock android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface mISupplicantStaIfaceMockV1_2; + private @Mock android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + mISupplicantStaIfaceMockV13; private @Mock Context mContext; private @Mock WifiMonitor mWifiMonitor; private @Mock PropertyService mPropertyService; private @Mock SupplicantStaNetworkHal mSupplicantStaNetworkMock; private @Mock WifiNative.SupplicantDeathEventHandler mSupplicantHalDeathHandler; + private @Mock Clock mClock; SupplicantStatus mStatusSuccess; SupplicantStatus mStatusFailure; @@ -140,7 +150,10 @@ public class SupplicantStaIfaceHalTest { mISupplicantStaIfaceCallbackV1_1; android.hardware.wifi.supplicant.V1_2.ISupplicantStaIfaceCallback mISupplicantStaIfaceCallbackV1_2; + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback + mISupplicantStaIfaceCallbackV13 = null; private TestLooper mLooper = new TestLooper(); + private Handler mHandler = null; private SupplicantStaIfaceHal mDut; private ArgumentCaptor<IHwBinder.DeathRecipient> mServiceManagerDeathCaptor = ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class); @@ -155,7 +168,8 @@ public class SupplicantStaIfaceHalTest { private class SupplicantStaIfaceHalSpy extends SupplicantStaIfaceHal { SupplicantStaIfaceHalSpy() { - super(mContext, mWifiMonitor, mPropertyService, mLooper.getLooper()); + super(mContext, mWifiMonitor, mPropertyService, + mHandler, mClock); } @Override @@ -192,6 +206,14 @@ public class SupplicantStaIfaceHalTest { } @Override + protected android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + getStaIfaceMockableV1_3(ISupplicantIface iface) { + return (mISupplicantMockV13 != null) + ? mISupplicantStaIfaceMockV13 + : null; + } + + @Override protected SupplicantStaNetworkHal getStaNetworkMockable( @NonNull String ifaceName, ISupplicantStaNetwork iSupplicantStaNetwork) { @@ -212,7 +234,6 @@ public class SupplicantStaIfaceHalTest { mIfaceInfoList.add(mStaIface0); mIfaceInfoList.add(mStaIface1); mIfaceInfoList.add(mP2pIface); - when(mServiceManagerMock.getTransport(anyString(), anyString())) .thenReturn(IServiceManager.Transport.EMPTY); when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class), @@ -221,6 +242,7 @@ public class SupplicantStaIfaceHalTest { any(IServiceNotification.Stub.class))).thenReturn(true); when(mISupplicantMock.linkToDeath(any(IHwBinder.DeathRecipient.class), anyLong())).thenReturn(true); + mHandler = spy(new Handler(mLooper.getLooper())); mDut = new SupplicantStaIfaceHalSpy(); } @@ -275,10 +297,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testInitialize_successV1_1() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_1(); executeAndValidateInitializationSequenceV1_1(false, false); } @@ -288,27 +307,27 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testInitialize_successV1_2() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_2.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_2 = mock(android.hardware.wifi.supplicant.V1_2.ISupplicant.class); - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_2(); executeAndValidateInitializationSequenceV1_2(); } /** + * Sunny day scenario for SupplicantStaIfaceHal initialization + * Asserts successful initialization + */ + @Test + public void testInitialize_successV1_3() throws Exception { + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + } + + /** * Tests the initialization flow, with a RemoteException occurring when 'getInterface' is called * Ensures initialization fails. */ @Test public void testInitialize_remoteExceptionFailureV1_1() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_1(); executeAndValidateInitializationSequenceV1_1(true, false); } @@ -318,10 +337,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testInitialize_nullInterfaceFailureV1_1() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_1(); executeAndValidateInitializationSequenceV1_1(false, true); } @@ -348,10 +364,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testDuplicateSetupIfaceV1_1_Fails() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_1(); executeAndValidateInitializationSequenceV1_1(false, false); // Trying setting up the wlan0 interface again & ensure it fails. @@ -422,240 +435,6 @@ public class SupplicantStaIfaceHalTest { verify(mISupplicantStaIfaceMock, never()).cancelWps(); } - - /** - * 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(WLAN0_IFACE_NAME, 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(WLAN0_IFACE_NAME, 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(WLAN0_IFACE_NAME, 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(WLAN0_IFACE_NAME, 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(WLAN0_IFACE_NAME, 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(WLAN0_IFACE_NAME, configs, extras)); - assertTrue(configs.isEmpty()); - } - /** * Tests connection to a specified network with empty existing network. */ @@ -1694,10 +1473,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testStartDaemonV1_1() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_1(); executeAndValidateInitializationSequenceV1_1(false, false); assertTrue(mDut.startDaemon()); @@ -1720,10 +1496,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testTerminateV1_1() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_1(); executeAndValidateInitializationSequenceV1_1(false, false); mDut.terminate(); @@ -1749,10 +1522,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testGetKeyMgmtCapabilitiesOldHal() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_1(); executeAndValidateInitializationSequenceV1_1(false, false); @@ -1764,14 +1534,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testGetKeyMgmtCapabilitiesWpa3Sae() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_2.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_2 = mock(android.hardware.wifi.supplicant.V1_2.ISupplicant.class); - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_2(); executeAndValidateInitializationSequenceV1_2(); @@ -1789,14 +1552,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testGetKeyMgmtCapabilitiesWpa3SuiteB() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_2.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_2 = mock(android.hardware.wifi.supplicant.V1_2.ISupplicant.class); - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_2(); executeAndValidateInitializationSequenceV1_2(); @@ -1815,14 +1571,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testGetKeyMgmtCapabilitiesOwe() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_2.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_2 = mock(android.hardware.wifi.supplicant.V1_2.ISupplicant.class); - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_2(); executeAndValidateInitializationSequenceV1_2(); @@ -1840,14 +1589,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testGetKeyMgmtCapabilitiesOweAndSae() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_2.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_2 = mock(android.hardware.wifi.supplicant.V1_2.ISupplicant.class); - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_2(); executeAndValidateInitializationSequenceV1_2(); @@ -1867,14 +1609,7 @@ public class SupplicantStaIfaceHalTest { */ @Test public void testGetKeyMgmtCapabilitiesDpp() throws Exception { - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_2.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_2 = mock(android.hardware.wifi.supplicant.V1_2.ISupplicant.class); - when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant - .kInterfaceName), anyString())) - .thenReturn(IServiceManager.Transport.HWBINDER); - mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + setupMocksForHalV1_2(); executeAndValidateInitializationSequenceV1_2(); @@ -1901,6 +1636,139 @@ public class SupplicantStaIfaceHalTest { assertFalse(mDut.startDppEnrolleeInitiator(WLAN0_IFACE_NAME, 3, 14)); } + /** + * Test adding PMK cache entry to the supplicant. + */ + @Test + public void testSetPmkSuccess() throws Exception { + int testFrameworkNetworkId = 9; + long testStartSeconds = PMK_CACHE_EXPIRATION_IN_SEC / 2; + WifiConfiguration config = new WifiConfiguration(); + config.networkId = testFrameworkNetworkId; + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + PmkCacheStoreData pmkCacheData = + new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>()); + mDut.mPmkCacheEntries.put(testFrameworkNetworkId, pmkCacheData); + when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); + + setupMocksForHalV1_3(); + setupMocksForPmkCache(); + setupMocksForConnectSequence(false); + + executeAndValidateInitializationSequenceV1_3(); + assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config)); + + verify(mSupplicantStaNetworkMock).setPmkCache(eq(pmkCacheData.data)); + verify(mISupplicantStaIfaceCallbackV13) + .onPmkCacheAdded(eq(PMK_CACHE_EXPIRATION_IN_SEC), eq(pmkCacheData.data)); + // there is only one cache entry, the next expiration alarm should be the same as + // its expiration time. + verify(mHandler).postDelayed( + /* private listener */ any(), + eq(SupplicantStaIfaceHal.PMK_CACHE_EXPIRATION_ALARM_TAG), + eq((PMK_CACHE_EXPIRATION_IN_SEC - testStartSeconds) * 1000)); + } + + /** + * Test adding PMK cache entry is not called if there is no + * valid PMK cache for a corresponding configuratoin. + */ + @Test + public void testAddPmkEntryNotCalledIfNoPmkCache() throws Exception { + int testFrameworkNetworkId = 9; + long testStartSeconds = PMK_CACHE_EXPIRATION_IN_SEC / 2; + WifiConfiguration config = new WifiConfiguration(); + config.networkId = testFrameworkNetworkId; + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); + + setupMocksForHalV1_3(); + setupMocksForPmkCache(); + setupMocksForConnectSequence(false); + executeAndValidateInitializationSequenceV1_3(); + assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config)); + + verify(mSupplicantStaNetworkMock, never()).setPmkCache(any(ArrayList.class)); + verify(mISupplicantStaIfaceCallbackV13, never()).onPmkCacheAdded( + anyLong(), any(ArrayList.class)); + verify(mHandler, never()).postDelayed( + /* private listener */ any(), + eq(SupplicantStaIfaceHal.PMK_CACHE_EXPIRATION_ALARM_TAG), + anyLong()); + } + + /** + * Test adding PMK cache entry returns faliure if HAL version is less than 1_3 + */ + @Test + public void testAddPmkEntryIsOmittedWithOldHal() throws Exception { + int testFrameworkNetworkId = 9; + long testStartSeconds = PMK_CACHE_EXPIRATION_IN_SEC / 2; + WifiConfiguration config = new WifiConfiguration(); + config.networkId = testFrameworkNetworkId; + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + PmkCacheStoreData pmkCacheData = + new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>()); + mDut.mPmkCacheEntries.put(testFrameworkNetworkId, pmkCacheData); + when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); + + setupMocksForConnectSequence(false); + executeAndValidateInitializationSequence(); + assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config)); + + verify(mSupplicantStaNetworkMock).setPmkCache(eq(pmkCacheData.data)); + assertNull(mISupplicantStaIfaceCallbackV13); + verify(mHandler, never()).postDelayed( + /* private listener */ any(), + eq(SupplicantStaIfaceHal.PMK_CACHE_EXPIRATION_ALARM_TAG), + anyLong()); + } + + /** + * Test getWifiTechnology + * Should fail if running HAL lower than V1_3 + */ + @Test + public void testGetWifiTechnologyV1_2() throws Exception { + setupMocksForHalV1_2(); + executeAndValidateInitializationSequenceV1_2(); + + assertEquals(WifiInfo.WIFI_TECHNOLOGY_UNKNOWN, mDut.getWifiTechnology(WLAN0_IFACE_NAME)); + } + + private class GetConnCapabilitiesAnswer extends MockAnswerUtil.AnswerWithArguments { + private ConnectionCapabilities mConnCapabilities; + + GetConnCapabilitiesAnswer(int wifiTechnology) { + mConnCapabilities = new ConnectionCapabilities(); + mConnCapabilities.technology = wifiTechnology; + } + + public void answer(android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + .getConnectionCapabilitiesCallback cb) { + cb.onValues(mStatusSuccess, mConnCapabilities); + } + } + + /** + * Test getWifiTechnology if running with HAL V1_3 + */ + @Test + public void testGetWifiTechnologyV1_3() throws Exception { + setupMocksForHalV1_3(); + + executeAndValidateInitializationSequenceV1_3(); + int testWifiTechnologyHal = WifiTechnology.VHT; + int testWifiTechnologyWifiInfo = WifiInfo.WIFI_TECHNOLOGY_11AC; + + doAnswer(new GetConnCapabilitiesAnswer(testWifiTechnologyHal)) + .when(mISupplicantStaIfaceMockV13).getConnectionCapabilities(any( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + .getConnectionCapabilitiesCallback.class)); + + assertEquals(testWifiTechnologyWifiInfo, mDut.getWifiTechnology(WLAN0_IFACE_NAME)); + } + private WifiConfiguration createTestWifiConfiguration() { WifiConfiguration config = new WifiConfiguration(); config.networkId = SUPPLICANT_NETWORK_ID; @@ -2152,6 +2020,60 @@ public class SupplicantStaIfaceHalTest { // any(ISupplicant.getInterfaceCallback.class)); } + /** + * 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 executeAndValidateInitializationSequenceV1_3() + throws Exception { + // Setup callback mock answers + doAnswer(new GetAddInterfaceAnswerV1_3(false)) + .when(mISupplicantMockV1_1).addInterface(any(ISupplicant.IfaceInfo.class), + any(android.hardware.wifi.supplicant.V1_1.ISupplicant + .addInterfaceCallback.class)); + + /** Callback registration */ + doAnswer(new MockAnswerUtil.AnswerWithArguments() { + public SupplicantStatus answer( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback cb) + throws RemoteException { + mISupplicantStaIfaceCallbackV13 = spy(cb); + return mStatusSuccess; + } + }).when(mISupplicantStaIfaceMockV13) + .registerCallback_1_3( + any(android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback + .class)); + + mInOrder = inOrder(mServiceManagerMock, mISupplicantMock, mISupplicantMockV1_1, + mISupplicantStaIfaceMockV13, 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()); + assertTrue(mDut.setupIface(WLAN0_IFACE_NAME)); + mInOrder.verify(mISupplicantMock).linkToDeath(mSupplicantDeathCaptor.capture(), + anyLong()); + // verify: addInterface is called + mInOrder.verify(mISupplicantMockV1_1) + .addInterface(any(ISupplicant.IfaceInfo.class), + any(android.hardware.wifi.supplicant.V1_1.ISupplicant + .addInterfaceCallback.class)); + + mInOrder.verify(mISupplicantStaIfaceMockV13) + .registerCallback_1_3( + any(android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback + .class)); + } + private SupplicantStatus createSupplicantStatus(int code) { SupplicantStatus status = new SupplicantStatus(); status.code = code; @@ -2232,6 +2154,24 @@ public class SupplicantStaIfaceHalTest { } } + private class GetAddInterfaceAnswerV1_3 extends MockAnswerUtil.AnswerWithArguments { + boolean mGetNullInterface; + + GetAddInterfaceAnswerV1_3(boolean getNullInterface) { + mGetNullInterface = getNullInterface; + } + + public void answer(ISupplicant.IfaceInfo iface, + android.hardware.wifi.supplicant.V1_3.ISupplicant + .addInterfaceCallback cb) { + if (mGetNullInterface) { + cb.onValues(mStatusSuccess, null); + } else { + cb.onValues(mStatusSuccess, mISupplicantIfaceMock); + } + } + } + /** * Setup mocks for connect sequence. */ @@ -2360,4 +2300,65 @@ public class SupplicantStaIfaceHalTest { verify(mISupplicantStaIfaceMock).reassociate(); } } + + /** + * Helper function to set up Hal cascadingly. + */ + private void setupMocksForHalV1_1() throws Exception { + // V1_0 is set up by default, no need to do it. + when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_1.ISupplicant + .kInterfaceName), anyString())) + .thenReturn(IServiceManager.Transport.HWBINDER); + mISupplicantMockV1_1 = mock(android.hardware.wifi.supplicant.V1_1.ISupplicant.class); + } + + private void setupMocksForHalV1_2() throws Exception { + setupMocksForHalV1_1(); + when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_2.ISupplicant + .kInterfaceName), anyString())) + .thenReturn(IServiceManager.Transport.HWBINDER); + mISupplicantMockV1_2 = mock(android.hardware.wifi.supplicant.V1_2.ISupplicant.class); + } + + private void setupMocksForHalV1_3() throws Exception { + setupMocksForHalV1_2(); + when(mServiceManagerMock.getTransport(eq(android.hardware.wifi.supplicant.V1_3.ISupplicant + .kInterfaceName), anyString())) + .thenReturn(IServiceManager.Transport.HWBINDER); + mISupplicantMockV13 = mock(android.hardware.wifi.supplicant.V1_3.ISupplicant.class); + } + + private void setupMocksForPmkCache() throws Exception { + /** Callback registration */ + doAnswer(new MockAnswerUtil.AnswerWithArguments() { + public SupplicantStatus answer( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback cb) + throws RemoteException { + mISupplicantStaIfaceCallbackV13 = cb; + return mStatusSuccess; + } + }).when(mISupplicantStaIfaceMockV13) + .registerCallback_1_3( + any(android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback + .class)); + + doAnswer(new MockAnswerUtil.AnswerWithArguments() { + public boolean answer(WifiConfiguration config, Map<String, String> networkExtra) + throws Exception { + config.networkId = SUPPLICANT_NETWORK_ID; + return true; + } + }).when(mSupplicantStaNetworkMock) + .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class)); + + doAnswer(new MockAnswerUtil.AnswerWithArguments() { + public boolean answer(ArrayList<Byte> serializedData) + throws Exception { + mISupplicantStaIfaceCallbackV13.onPmkCacheAdded( + PMK_CACHE_EXPIRATION_IN_SEC, serializedData); + return true; + } + }).when(mSupplicantStaNetworkMock) + .setPmkCache(any(ArrayList.class)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java index 796dce1dfa..1013318687 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java @@ -19,6 +19,7 @@ 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.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Matchers.eq; @@ -62,7 +63,7 @@ import java.util.Random; * Unit tests for SupplicantStaNetworkHal */ @SmallTest -public class SupplicantStaNetworkHalTest { +public class SupplicantStaNetworkHalTest extends WifiBaseTest { private static final String IFACE_NAME = "wlan0"; private static final Map<String, String> NETWORK_EXTRAS_VALUES = new HashMap<>(); static { @@ -79,6 +80,8 @@ public class SupplicantStaNetworkHalTest { @Mock private ISupplicantStaNetwork mISupplicantStaNetworkMock; @Mock private android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork mISupplicantStaNetworkV12; + @Mock + private android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork mISupplicantStaNetworkV13; @Mock private Context mContext; @Mock private WifiMonitor mWifiMonitor; @@ -86,6 +89,12 @@ public class SupplicantStaNetworkHalTest { private MockResources mResources; private ISupplicantStaNetworkCallback mISupplicantStaNetworkCallback; + enum SupplicantStaNetworkVersion { + V1_0, + V1_2, + V1_3, + } + /** * Spy used to return the V1_2 ISupplicantStaNetwork mock object to simulate the 1.2 HAL running * on the device. @@ -104,6 +113,24 @@ public class SupplicantStaNetworkHalTest { } } + /** + * Spy used to return the V1_3 ISupplicantStaNetwork mock object to simulate the 1.3 HAL running + * on the device. + */ + private class SupplicantStaNetworkHalSpyV1_3 extends SupplicantStaNetworkHalSpyV1_2 { + SupplicantStaNetworkHalSpyV1_3(ISupplicantStaNetwork iSupplicantStaNetwork, + String ifaceName, + Context context, WifiMonitor monitor) { + super(iSupplicantStaNetwork, ifaceName, context, monitor); + } + + @Override + protected android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + getSupplicantStaNetworkForV1_3Mockable() { + return mISupplicantStaNetworkV13; + } + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -114,7 +141,7 @@ public class SupplicantStaNetworkHalTest { mResources = new MockResources(); when(mContext.getResources()).thenReturn(mResources); - createSupplicantStaNetwork(); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_0); } /** @@ -123,8 +150,7 @@ public class SupplicantStaNetworkHalTest { @Test public void testOweNetworkWifiConfigurationSaveLoad() throws Exception { // Now expose the V1.2 ISupplicantStaNetwork - mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2(mISupplicantStaNetworkMock, - IFACE_NAME, mContext, mWifiMonitor); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_2); WifiConfiguration config = WifiConfigurationTestUtil.createOweNetwork(); config.updateIdentifier = "46"; @@ -147,15 +173,39 @@ public class SupplicantStaNetworkHalTest { @Test public void testSaePasswordNetworkWifiConfigurationSaveLoad() throws Exception { // Now expose the V1.2 ISupplicantStaNetwork + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_2); + + WifiConfiguration config = WifiConfigurationTestUtil.createSaeNetwork(); + testWifiConfigurationSaveLoad(config); + verify(mISupplicantStaNetworkV12).setSaePassword(any(String.class)); + verify(mISupplicantStaNetworkV12, never()) + .getSaePassword(any(android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork + .getSaePasswordCallback.class)); + verify(mISupplicantStaNetworkV12, never()).setSaePasswordId(any(String.class)); + verify(mISupplicantStaNetworkV12, never()) + .getPskPassphrase(any(ISupplicantStaNetwork.getPskPassphraseCallback.class)); + verify(mISupplicantStaNetworkV12, never()).setPsk(any(byte[].class)); + verify(mISupplicantStaNetworkV12, never()) + .getPsk(any(ISupplicantStaNetwork.getPskCallback.class)); + } + + /** + * Tests the saving/loading of WifiConfiguration to wpa_supplicant with SAE password identifier. + */ + @Test + public void testSaePasswordIdNetworkWifiConfigurationSaveLoad() throws Exception { + // Now expose the V1.2 ISupplicantStaNetwork mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2(mISupplicantStaNetworkMock, IFACE_NAME, mContext, mWifiMonitor); WifiConfiguration config = WifiConfigurationTestUtil.createSaeNetwork(); + config.saePasswordId = "TestIdentifier"; testWifiConfigurationSaveLoad(config); verify(mISupplicantStaNetworkV12).setSaePassword(any(String.class)); verify(mISupplicantStaNetworkV12, never()) .getSaePassword(any(android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork .getSaePasswordCallback.class)); + verify(mISupplicantStaNetworkV12).setSaePasswordId(any(String.class)); verify(mISupplicantStaNetworkV12, never()) .getPskPassphrase(any(ISupplicantStaNetwork.getPskPassphraseCallback.class)); verify(mISupplicantStaNetworkV12, never()).setPsk(any(byte[].class)); @@ -257,6 +307,23 @@ public class SupplicantStaNetworkHalTest { * Tests the saving of WifiConfiguration to wpa_supplicant. */ @Test + public void testEapTlsNoneClientCertNetworkWithOcspWifiConfigurationSaveLoad() + throws Exception { + // Now expose the V1.3 ISupplicantStaNetwork + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_3); + + WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); + config.enterpriseConfig = + WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2(); + config.enterpriseConfig.setClientCertificateAlias("test_alias"); + config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); + testWifiConfigurationSaveLoad(config); + } + + /** + * Tests the saving of WifiConfiguration to wpa_supplicant. + */ + @Test public void testEapTlsAkaNetworkWifiConfigurationSaveLoad() throws Exception { WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); config.enterpriseConfig = @@ -270,8 +337,7 @@ public class SupplicantStaNetworkHalTest { @Test public void testEapSuiteBRsaNetworkWifiConfigurationSaveLoad() throws Exception { // Now expose the V1.2 ISupplicantStaNetwork - mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2(mISupplicantStaNetworkMock, - IFACE_NAME, mContext, mWifiMonitor); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_2); WifiConfiguration config = WifiConfigurationTestUtil.createEapSuiteBNetwork(); config.allowedSuiteBCiphers.set(WifiConfiguration.SuiteBCipher.ECDHE_RSA); @@ -297,8 +363,7 @@ public class SupplicantStaNetworkHalTest { @Test public void testEapSuiteBEcdsaNetworkWifiConfigurationSaveLoad() throws Exception { // Now expose the V1.2 ISupplicantStaNetwork - mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2(mISupplicantStaNetworkMock, - IFACE_NAME, mContext, mWifiMonitor); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_2); WifiConfiguration config = WifiConfigurationTestUtil.createEapSuiteBNetwork(); config.allowedSuiteBCiphers.set(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); @@ -320,42 +385,6 @@ public class SupplicantStaNetworkHalTest { } /** - * 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 @@ -388,27 +417,6 @@ public class SupplicantStaNetworkHalTest { } /** - * Tests the failure to load invalid key mgmt (unknown 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). */ @@ -425,81 +433,6 @@ public class SupplicantStaNetworkHalTest { } /** - * 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 @@ -732,8 +665,7 @@ public class SupplicantStaNetworkHalTest { any(ArrayList.class), any(ArrayList.class)); // Now expose the V1.2 ISupplicantStaNetwork - mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2(mISupplicantStaNetworkMock, - IFACE_NAME, mContext, mWifiMonitor); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_2); doAnswer(new AnswerWithArguments() { public SupplicantStatus answer(ArrayList<Byte> identity, ArrayList<Byte> encryptedIdentity) @@ -755,7 +687,7 @@ public class SupplicantStaNetworkHalTest { @Test public void testAddFtPskFlags() throws Exception { mResources.setBoolean(R.bool.config_wifi_fast_bss_transition_enabled, true); - createSupplicantStaNetwork(); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_0); WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(); assertTrue(mSupplicantNetwork.saveWifiConfiguration(config)); @@ -777,7 +709,7 @@ public class SupplicantStaNetworkHalTest { @Test public void testAddFtEapFlags() throws Exception { mResources.setBoolean(R.bool.config_wifi_fast_bss_transition_enabled, true); - createSupplicantStaNetwork(); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_0); WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); assertTrue(mSupplicantNetwork.saveWifiConfiguration(config)); @@ -800,8 +732,7 @@ public class SupplicantStaNetworkHalTest { public void testAddPskSha256Flags() throws Exception { WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(); // Now expose the V1.2 ISupplicantStaNetwork - mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2(mISupplicantStaNetworkMock, - IFACE_NAME, mContext, mWifiMonitor); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_2); assertTrue(mSupplicantNetwork.saveWifiConfiguration(config)); // Check the supplicant variables to ensure that we have added the SHA256 flags. @@ -824,8 +755,7 @@ public class SupplicantStaNetworkHalTest { public void testAddEapSha256Flags() throws Exception { WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); // Now expose the V1.2 ISupplicantStaNetwork - mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2(mISupplicantStaNetworkMock, - IFACE_NAME, mContext, mWifiMonitor); + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_2); assertTrue(mSupplicantNetwork.saveWifiConfiguration(config)); // Check the supplicant variables to ensure that we have added the SHA256 flags. @@ -872,6 +802,23 @@ public class SupplicantStaNetworkHalTest { } /** + * Tests OCSP status is ignored on HAL v1.2 or lower + */ + @Test + public void testOcspStatusHal1_2OrLower() throws Exception { + WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); + config.enterpriseConfig = + WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2(); + config.enterpriseConfig.setClientCertificateAlias("test_alias"); + config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); + + assertTrue(mSupplicantNetwork.saveWifiConfiguration(config)); + + // Check the supplicant variables to ensure that we have NOT change the OCSP status. + assertEquals(WifiEnterpriseConfig.OCSP_NONE, mSupplicantVariables.ocsp); + } + + /** * Tests the retrieval of WPS NFC token. */ @Test @@ -1026,6 +973,35 @@ public class SupplicantStaNetworkHalTest { assertEquals(ANONYMOUS_IDENTITY, mSupplicantNetwork.fetchEapAnonymousIdentity()); } + /** Verifies that setPmkCache can set PMK cache + * + */ + public void testSetPmkCache() { + WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); + config.enterpriseConfig = + WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2(); + assertTrue(mSupplicantNetwork.saveWifiConfiguration(config)); + + ArrayList<Byte> serializedData = new ArrayList<>(); + assertTrue(mSupplicantNetwork.setPmkCache(serializedData)); + assertEquals(serializedData, mSupplicantVariables.serializedPmkCache); + } + + /** + * Tests PMK cache is not set on HAL v1.2 or lower + */ + @Test + public void testSetPmkCacheHal1_2OrLower() throws Exception { + WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); + config.enterpriseConfig = + WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2(); + assertTrue(mSupplicantNetwork.saveWifiConfiguration(config)); + + ArrayList<Byte> serializedData = new ArrayList<>(); + assertFalse(mSupplicantNetwork.setPmkCache(serializedData)); + assertNull(mSupplicantVariables.serializedPmkCache); + } + /** * 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. @@ -1111,6 +1087,22 @@ public class SupplicantStaNetworkHalTest { }).when(mISupplicantStaNetworkV12) .getSaePassword(any(android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork .getSaePasswordCallback.class)); + /** SAE password identifier*/ + doAnswer(new AnswerWithArguments() { + public SupplicantStatus answer(String saePasswordId) throws RemoteException { + mSupplicantVariables.saePasswordId = saePasswordId; + return mStatusSuccess; + } + }).when(mISupplicantStaNetworkV12).setSaePasswordId(any(String.class)); + doAnswer(new AnswerWithArguments() { + public void answer(android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork + .getSaePasswordIdCallback cb) + throws RemoteException { + cb.onValues(mStatusSuccess, mSupplicantVariables.saePasswordId); + } + }).when(mISupplicantStaNetworkV12) + .getSaePasswordId(any(android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork + .getSaePasswordIdCallback.class)); /** PSK passphrase */ doAnswer(new AnswerWithArguments() { @@ -1565,6 +1557,32 @@ public class SupplicantStaNetworkHalTest { return mStatusSuccess; } }).when(mISupplicantStaNetworkV12).enableSuiteBEapOpenSslCiphers(); + + /** OCSP */ + doAnswer(new AnswerWithArguments() { + public SupplicantStatus answer(int ocsp) throws RemoteException { + mSupplicantVariables.ocsp = ocsp; + return mStatusSuccess; + } + }).when(mISupplicantStaNetworkV13).setOcsp(any(int.class)); + doAnswer(new AnswerWithArguments() { + public void answer( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.getOcspCallback cb) + throws RemoteException { + cb.onValues(mStatusSuccess, mSupplicantVariables.ocsp); + } + }).when(mISupplicantStaNetworkV13) + .getOcsp(any(android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + .getOcspCallback.class)); + + /** PMK cache */ + doAnswer(new AnswerWithArguments() { + public SupplicantStatus answer(ArrayList<Byte> serializedData) throws RemoteException { + mSupplicantVariables.serializedPmkCache = serializedData; + return mStatusSuccess; + } + }).when(mISupplicantStaNetworkV13).setPmkCache(any(ArrayList.class)); + } private SupplicantStatus createSupplicantStatus(int code) { @@ -1576,10 +1594,21 @@ public class SupplicantStaNetworkHalTest { /** * 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 void createSupplicantStaNetwork(SupplicantStaNetworkVersion version) { + switch (version) { + case V1_0: + mSupplicantNetwork = new SupplicantStaNetworkHal( + mISupplicantStaNetworkMock, IFACE_NAME, mContext, mWifiMonitor); + break; + case V1_2: + mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_2( + mISupplicantStaNetworkMock, IFACE_NAME, mContext, mWifiMonitor); + break; + case V1_3: + mSupplicantNetwork = new SupplicantStaNetworkHalSpyV1_3( + mISupplicantStaNetworkMock, IFACE_NAME, mContext, mWifiMonitor); + break; + } mSupplicantNetwork.enableVerboseLogging(true); } @@ -1598,6 +1627,7 @@ public class SupplicantStaNetworkHalTest { public String idStr; public int updateIdentifier; public String pskPassphrase; + public String saePasswordId; public byte[] psk; public ArrayList<Byte>[] wepKey = new ArrayList[4]; public int wepTxKeyIdx; @@ -1616,5 +1646,7 @@ public class SupplicantStaNetworkHalTest { public String eapEngineID; public String eapDomainSuffixMatch; public boolean eapProactiveKeyCaching; + public int ocsp; + public ArrayList<Byte> serializedPmkCache; } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStateTrackerTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStateTrackerTest.java index d0713cc0f6..023d16d690 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStateTrackerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStateTrackerTest.java @@ -43,7 +43,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link android.net.wifi.SupplicantStateTracker}. */ @SmallTest -public class SupplicantStateTrackerTest { +public class SupplicantStateTrackerTest extends WifiBaseTest { private static final String TAG = "SupplicantStateTrackerTest"; private static final String sSSID = "\"GoogleGuest\""; diff --git a/service/tests/wifitests/src/com/android/server/wifi/UntrustedWifiNetworkFactoryTest.java b/service/tests/wifitests/src/com/android/server/wifi/UntrustedWifiNetworkFactoryTest.java index 5ef629254d..012889d4b8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/UntrustedWifiNetworkFactoryTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/UntrustedWifiNetworkFactoryTest.java @@ -35,7 +35,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.UntrustedWifiNetworkFactory}. */ @SmallTest -public class UntrustedWifiNetworkFactoryTest { +public class UntrustedWifiNetworkFactoryTest extends WifiBaseTest { @Mock WifiConnectivityManager mWifiConnectivityManager; @Mock Context mContext; NetworkCapabilities mNetworkCapabilities; diff --git a/service/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java index 5fa0188bc7..1de1600848 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java @@ -38,7 +38,7 @@ import org.mockito.Spy; * Unit tests for {@link com.android.server.wifi.VelocityBasedConnectedScore}. */ @SmallTest -public class VelocityBasedConnectedScoreTest { +public class VelocityBasedConnectedScoreTest extends WifiBaseTest { class FakeClock extends Clock { diff --git a/service/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java index c814aef1a8..7519de0644 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java @@ -48,7 +48,7 @@ import java.util.Set; * Unit tests for {@link WakeupConfigStoreData}. */ @SmallTest -public class WakeupConfigStoreDataTest { +public class WakeupConfigStoreDataTest extends WifiBaseTest { @Mock private WakeupConfigStoreData.DataSource<Boolean> mActiveDataSource; @Mock private WakeupConfigStoreData.DataSource<Boolean> mIsOnboardedDataSource; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java index 009429b3f0..112d2fb74a 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java @@ -32,6 +32,7 @@ import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiScanner; +import android.os.Handler; import android.os.test.TestLooper; import android.provider.Settings; @@ -60,7 +61,7 @@ import java.util.Set; * Unit tests for {@link WakeupController}. */ @SmallTest -public class WakeupControllerTest { +public class WakeupControllerTest extends WifiBaseTest { private static final String SAVED_SSID = "test scan ssid"; private static final int DFS_CHANNEL_FREQ = 5540; @@ -77,7 +78,7 @@ public class WakeupControllerTest { @Mock private FrameworkFacade mFrameworkFacade; @Mock private WifiSettingsStore mWifiSettingsStore; @Mock private WifiWakeMetrics mWifiWakeMetrics; - @Mock private WifiController mWifiController; + @Mock private ActiveModeWarden mActiveModeWarden; @Mock private WifiNative mWifiNative; @Mock private Clock mClock; @@ -94,7 +95,7 @@ public class WakeupControllerTest { when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner); when(mWifiInjector.getWifiSettingsStore()).thenReturn(mWifiSettingsStore); - when(mWifiInjector.getWifiController()).thenReturn(mWifiController); + when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative); when(mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY)) .thenReturn(new int[]{DFS_CHANNEL_FREQ}); @@ -126,7 +127,7 @@ public class WakeupControllerTest { Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(settingsValue); when(mWakeupOnboarding.isOnboarded()).thenReturn(true); mWakeupController = new WakeupController(mContext, - mLooper.getLooper(), + new Handler(mLooper.getLooper()), mWakeupLock, mWakeupEvaluator, mWakeupOnboarding, @@ -363,10 +364,10 @@ public class WakeupControllerTest { // suggestions WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(quotedSsid); WifiNetworkSuggestion openNetworkSuggestion = - new WifiNetworkSuggestion(openNetwork, false, false, -1, ""); + new WifiNetworkSuggestion(openNetwork, null, false, false, -1, ""); WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork(); WifiNetworkSuggestion wepNetworkSuggestion = - new WifiNetworkSuggestion(wepNetwork, false, false, -1, ""); + new WifiNetworkSuggestion(wepNetwork, null, false, false, -1, ""); when(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions()) .thenReturn(new HashSet<>(Arrays.asList( openNetworkSuggestion, wepNetworkSuggestion))); @@ -408,7 +409,7 @@ public class WakeupControllerTest { WifiConfiguration oweNetwork = WifiConfigurationTestUtil.createOweNetwork(quotedSsid2); WifiNetworkSuggestion oweNetworkSuggestion = - new WifiNetworkSuggestion(oweNetwork, false, false, -1, ""); + new WifiNetworkSuggestion(oweNetwork, null, false, false, -1, ""); when(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions()) .thenReturn(new HashSet<>(Arrays.asList(oweNetworkSuggestion))); @@ -506,7 +507,7 @@ public class WakeupControllerTest { WifiConfiguration openNetwork = WifiConfigurationTestUtil .createOpenNetwork(ScanResultUtil.createQuotedSSID(SAVED_SSID)); WifiNetworkSuggestion openNetworkSuggestion = - new WifiNetworkSuggestion(openNetwork, false, false, -1, ""); + new WifiNetworkSuggestion(openNetwork, null, false, false, -1, ""); when(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions()) .thenReturn(new HashSet<>(Collections.singletonList(openNetworkSuggestion))); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java index 2510c33beb..fcbc7e2fde 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java @@ -40,7 +40,7 @@ import java.util.Set; * Unit tests for {@link WakeupEvaluator}. */ @SmallTest -public class WakeupEvaluatorTest { +public class WakeupEvaluatorTest extends WifiBaseTest { private static final String SAVED_SSID_1 = "saved ssid 1"; private static final String SAVED_SSID_2 = "saved ssid 2"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java b/service/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java index b588d6ace9..7c53f49851 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java @@ -39,7 +39,7 @@ import java.util.List; * Unit tests for {@link WakeupLock}. */ @SmallTest -public class WakeupLockTest { +public class WakeupLockTest extends WifiBaseTest { private static final String SSID_1 = "ssid1"; private static final String SSID_2 = "ssid2"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WakeupOnboardingTest.java b/service/tests/wifitests/src/com/android/server/wifi/WakeupOnboardingTest.java index 605a08ff21..7d9cec22bb 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WakeupOnboardingTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WakeupOnboardingTest.java @@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations; /** Unit tests for {@link com.android.server.wifi.WakeupOnboarding} */ @SmallTest -public class WakeupOnboardingTest { +public class WakeupOnboardingTest extends WifiBaseTest { @Mock private Context mContext; @Mock private WifiConfigManager mWifiConfigManager; @@ -78,8 +78,8 @@ public class WakeupOnboardingTest { .thenReturn(mNotificationManager); mLooper = new TestLooper(); - mWakeupOnboarding = new WakeupOnboarding(mContext, mWifiConfigManager, mLooper.getLooper(), - mFrameworkFacade, mWakeupNotificationFactory); + mWakeupOnboarding = new WakeupOnboarding(mContext, mWifiConfigManager, + new Handler(mLooper.getLooper()), mFrameworkFacade, mWakeupNotificationFactory); } /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java index c182788938..8799ba6e2d 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java @@ -34,6 +34,7 @@ import android.content.pm.ApplicationInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.Build; +import android.os.Handler; import android.os.test.TestLooper; import androidx.test.filters.SmallTest; @@ -58,7 +59,7 @@ import java.util.Random; * Unit tests for {@link com.android.server.wifi.WifiApConfigStore}. */ @SmallTest -public class WifiApConfigStoreTest { +public class WifiApConfigStoreTest extends WifiBaseTest { private static final String TAG = "WifiApConfigStoreTest"; @@ -81,7 +82,8 @@ public class WifiApConfigStoreTest { private static final String TEST_STRING_UTF8_WITH_34_BYTES = "Ευπροσηγοροςγινου"; @Mock private Context mContext; - private TestLooper mLooper; + @Mock private WifiInjector mWifiInjector; + private Handler mHandler; @Mock private BackupManagerProxy mBackupManagerProxy; @Mock private FrameworkFacade mFrameworkFacade; private File mApConfigFile; @@ -94,7 +96,7 @@ public class WifiApConfigStoreTest { @Before public void setUp() throws Exception { - mLooper = new TestLooper(); + mHandler = new Handler(new TestLooper().getLooper()); MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)) .thenReturn(mNotificationManager); @@ -139,7 +141,7 @@ public class WifiApConfigStoreTest { */ private WifiApConfigStore createWifiApConfigStore() { WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = @@ -191,6 +193,7 @@ public class WifiApConfigStoreTest { int randomPortion = Integer.parseInt(splitSsid[1]); assertTrue(randomPortion >= RAND_SSID_INT_MIN && randomPortion <= RAND_SSID_INT_MAX); assertTrue(config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)); + assertEquals(15, config.preSharedKey.length()); } private void verifyDefaultLocalOnlyApConfig(WifiConfiguration config, String expectedSsid, @@ -202,6 +205,7 @@ public class WifiApConfigStoreTest { int randomPortion = Integer.parseInt(splitSsid[1]); assertTrue(randomPortion >= RAND_SSID_INT_MIN && randomPortion <= RAND_SSID_INT_MAX); assertTrue(config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)); + assertEquals(15, config.preSharedKey.length()); } @@ -212,7 +216,7 @@ public class WifiApConfigStoreTest { @Test public void initWithDefaultConfiguration() throws Exception { WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); } @@ -232,7 +236,7 @@ public class WifiApConfigStoreTest { true /* Hidden SSID */); writeApConfigFile(expectedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); } @@ -254,7 +258,7 @@ public class WifiApConfigStoreTest { true /* Hidden SSID */); writeApConfigFile(expectedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); @@ -270,7 +274,7 @@ public class WifiApConfigStoreTest { public void updateApConfiguration() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); @@ -296,7 +300,7 @@ public class WifiApConfigStoreTest { public void convertSingleModeDeviceAnyTo5Ghz() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); @@ -330,7 +334,7 @@ public class WifiApConfigStoreTest { public void singleModeDevice5GhzNotConverted() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); @@ -357,7 +361,7 @@ public class WifiApConfigStoreTest { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); @@ -393,7 +397,7 @@ public class WifiApConfigStoreTest { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); @@ -434,7 +438,7 @@ public class WifiApConfigStoreTest { false /* Hidden SSID */); writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); verify(mBackupManagerProxy).notifyDataChanged(); @@ -457,7 +461,7 @@ public class WifiApConfigStoreTest { writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyApConfig(persistedConfig, store.getApConfiguration()); verify(mBackupManagerProxy, never()).notifyDataChanged(); @@ -489,7 +493,7 @@ public class WifiApConfigStoreTest { writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); verify(mBackupManagerProxy).notifyDataChanged(); @@ -514,7 +518,7 @@ public class WifiApConfigStoreTest { writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); verifyApConfig(persistedConfig, store.getApConfiguration()); verify(mBackupManagerProxy, never()).notifyDataChanged(); @@ -526,7 +530,7 @@ public class WifiApConfigStoreTest { @Test public void getDefaultApConfigurationIsValid() { WifiApConfigStore store = new WifiApConfigStore( - mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, mApConfigFile.getPath()); WifiConfiguration config = store.getApConfiguration(); assertTrue(WifiApConfigStore.validateApWifiConfiguration(config)); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java index 9501143c89..45060a67e1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java @@ -51,7 +51,7 @@ import java.util.Random; * Unit tests for {@link com.android.server.wifi.WifiBackupRestore}. */ @SmallTest -public class WifiBackupRestoreTest { +public class WifiBackupRestoreTest extends WifiBaseTest { private static final String WIFI_BACKUP_DATA_WITH_UNSUPPORTED_TAG = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiBaseTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiBaseTest.java new file mode 100644 index 0000000000..a7fb733c77 --- /dev/null +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiBaseTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.junit.After; +import org.mockito.Mockito; + +/** + * Base class for Wifi unit tests that cleans up inline mocks. + */ +public abstract class WifiBaseTest { + /** + * Clean up inline mocks to prevent OutOfMemory errors. + * See https://github.com/mockito/mockito/issues/1614 + */ + @After + public final void clearInlineMocks() { + Mockito.framework().clearInlineMocks(); + } +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java index 275d90deb2..5c07edc279 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java @@ -36,7 +36,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiCandidates}. */ @SmallTest -public class WifiCandidatesTest { +public class WifiCandidatesTest extends WifiBaseTest { @Mock ScanDetail mScanDetail1; @Mock ScanDetail mScanDetail2; @@ -79,10 +79,10 @@ public class WifiCandidatesTest { */ @Test public void testDontDieFromNulls() throws Exception { - mWifiCandidates.add(null, mConfig1, 1, 42); - mWifiCandidates.add(mScanDetail1, null, 2, 16); + mWifiCandidates.add(null, mConfig1, 1, 42, 0.0, false); + mWifiCandidates.add(mScanDetail1, null, 2, 16, 0.0, false); doReturn(null).when(mScanDetail2).getScanResult(); - mWifiCandidates.add(mScanDetail2, mConfig2, 3, 314, 1.0); + mWifiCandidates.add(mScanDetail2, mConfig2, 3, 314, 1.0, true); assertFalse(mWifiCandidates.remove(null)); assertEquals(0, mWifiCandidates.size()); @@ -93,7 +93,7 @@ public class WifiCandidatesTest { */ @Test public void testAddJustOne() throws Exception { - assertTrue(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0)); + assertTrue(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, false)); assertEquals(1, mWifiCandidates.size()); assertEquals(0, mWifiCandidates.getFaultCount()); @@ -108,7 +108,7 @@ public class WifiCandidatesTest { public void testQuotingBotch() throws Exception { // Unfortunately ScanResult.SSID is not quoted; make sure we catch that mScanResult1.SSID = mConfig1.SSID; - mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0); + mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, true); // Should not have added this one assertEquals(0, mWifiCandidates.size()); @@ -136,7 +136,7 @@ public class WifiCandidatesTest { assertFalse(matchInfo1 == matchInfo1Prime); // Checking assumption MacAddress mac1 = MacAddress.createRandomUnicastAddress(); MacAddress mac2 = MacAddress.createRandomUnicastAddress(); - assertNotEquals(mac1, mac2); // really tiny probablility of failing here + assertNotEquals(mac1, mac2); // really tiny probability of failing here WifiCandidates.Key key1 = new WifiCandidates.Key(matchInfo1, mac1, 1); @@ -167,7 +167,7 @@ public class WifiCandidatesTest { assertTrue(mWifiCandidates == mWifiCandidates.setPicky(true)); try { mScanResult1.SSID = mConfig1.SSID; // As in testQuotingBotch() - mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0); + mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, false); fail("Exception not raised in picky mode"); } catch (IllegalArgumentException e) { assertEquals(1, mWifiCandidates.getFaultCount()); @@ -181,23 +181,23 @@ public class WifiCandidatesTest { @Test public void testNoOverwriteCases() throws Exception { // Setup is to add the first candidate - mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0); + mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, false); assertEquals(1, mWifiCandidates.size()); // Same evaluator, same score. Should not add. - assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0)); + assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, false)); assertEquals(0, mWifiCandidates.getFaultCount()); // But not considered a fault // Same evaluator, lower score. Should not add. - assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 13, 0.0)); + assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 13, 0.0, false)); assertEquals(0, mWifiCandidates.getFaultCount()); // Also not a fault // Later evaluator. Should not add (regardless of score). - assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 5, 13)); - assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 5, 15)); + assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 5, 13, 0.0, false)); + assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 5, 15, 0.0, false)); assertEquals(0, mWifiCandidates.getFaultCount()); // Still no faults // Evaluator out of order. Should not add (regardless of score). - assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 1, 12)); + assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 1, 12, 0.0, false)); assertNotNull(mWifiCandidates.getLastFault()); // This one is considered a caller error - assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 1, 15)); + assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 1, 15, 0.0, false)); assertEquals(2, mWifiCandidates.getFaultCount()); // After all that, only one candidate should be there. assertEquals(1, mWifiCandidates.size()); @@ -210,12 +210,12 @@ public class WifiCandidatesTest { public void testBssidValidation() throws Exception { // Null BSSID. mScanResult1.BSSID = null; - mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14); + mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, false); assertTrue("Expecting NPE, got " + mWifiCandidates.getLastFault(), mWifiCandidates.getLastFault() instanceof NullPointerException); // Malformed BSSID mScanResult1.BSSID = "NotaBssid!"; - mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14); + mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, false); assertTrue("Expecting IAE, got " + mWifiCandidates.getLastFault(), mWifiCandidates.getLastFault() instanceof IllegalArgumentException); assertEquals(0, mWifiCandidates.size()); @@ -232,8 +232,8 @@ public class WifiCandidatesTest { mScanResult2.SSID = mScanResult1.SSID; mScanResult2.BSSID = mScanResult1.BSSID.replace('1', '2'); // Add both - mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14); - mWifiCandidates.add(mScanDetail2, mConfig2, 2, 14); + mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0, false); + mWifiCandidates.add(mScanDetail2, mConfig2, 2, 14, 0.0, false); // We expect them both to be there assertEquals(2, mWifiCandidates.size()); // But just one group @@ -268,8 +268,8 @@ public class WifiCandidatesTest { mScanResult2.SSID = mScanResult1.SSID; mScanResult2.BSSID = mScanResult1.BSSID; // Try adding them both, the higher-scoring one second - assertTrue(mWifiCandidates.add(mScanDetail2, mConfig2, 2, 14)); - assertTrue(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 15)); + assertTrue(mWifiCandidates.add(mScanDetail2, mConfig2, 2, 14, 0.0, false)); + assertTrue(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 15, 0.0, false)); // Only one should survive assertEquals(1, mWifiCandidates.size()); // And no faults diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java index 686b2098de..9789e1bec0 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java @@ -20,14 +20,12 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.annotation.Nullable; -import android.app.admin.DeviceAdminInfo; -import android.app.admin.DevicePolicyManagerInternal; +import android.app.ActivityManager; 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.database.ContentObserver; import android.net.IpConfiguration; import android.net.MacAddress; @@ -38,6 +36,7 @@ import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; +import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; @@ -63,6 +62,7 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -80,7 +80,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.WifiConfigManager}. */ @SmallTest -public class WifiConfigManagerTest { +public class WifiConfigManagerTest extends WifiBaseTest { private static final String TEST_SSID = "\"test_ssid\""; private static final String TEST_BSSID = "0a:08:5c:67:89:00"; @@ -111,6 +111,8 @@ public class WifiConfigManagerTest { private static final int TEST_FREQUENCY_1 = 2412; private static final int TEST_FREQUENCY_2 = 5180; private static final int TEST_FREQUENCY_3 = 5240; + private static final MacAddress TEST_RANDOMIZED_MAC = + MacAddress.fromString("d2:11:19:34:a5:20"); @Mock private Context mContext; @Mock private Clock mClock; @@ -120,7 +122,6 @@ public class WifiConfigManagerTest { @Mock private WifiKeyStore mWifiKeyStore; @Mock private WifiConfigStore mWifiConfigStore; @Mock private PackageManager mPackageManager; - @Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal; @Mock private WifiPermissionsUtil mWifiPermissionsUtil; @Mock private WifiPermissionsWrapper mWifiPermissionsWrapper; @Mock private WifiInjector mWifiInjector; @@ -129,8 +130,9 @@ public class WifiConfigManagerTest { @Mock private NetworkListUserStoreData mNetworkListUserStoreData; @Mock private DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData; @Mock private RandomizedMacStoreData mRandomizedMacStoreData; - @Mock private WifiConfigManager.OnSavedNetworkUpdateListener mWcmListener; + @Mock private WifiConfigManager.OnNetworkUpdateListener mWcmListener; @Mock private FrameworkFacade mFrameworkFacade; + @Mock private DeviceConfigFacade mDeviceConfigFacade; @Mock private CarrierNetworkConfig mCarrierNetworkConfig; private MockResources mResources; @@ -187,15 +189,17 @@ public class WifiConfigManagerTest { 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(mContext.getSystemService(ActivityManager.class)) + .thenReturn(mock(ActivityManager.class)); + Context mockContext = mock(Context.class); + when(mContext.createPackageContextAsUser( + eq(WifiConfigManager.SYSUI_PACKAGE_NAME), anyInt(), any())) + .thenReturn(mockContext); + PackageManager mockPackageManager = mock(PackageManager.class); + when(mockContext.getPackageManager()).thenReturn(mockPackageManager); + when(mockPackageManager.getPackageUid(eq(WifiConfigManager.SYSUI_PACKAGE_NAME), anyInt())) + .thenReturn(TEST_SYSUI_UID); when(mWifiKeyStore .updateNetworkKeys(any(WifiConfiguration.class), any())) @@ -204,17 +208,15 @@ public class WifiConfigManagerTest { when(mWifiConfigStore.areStoresPresent()).thenReturn(true); setupStoreDataForRead(new ArrayList<>(), new ArrayList<>(), new HashMap<>()); - when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(), anyInt())) - .thenReturn(false); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); - when(mWifiPermissionsWrapper.getDevicePolicyManagerInternal()) - .thenReturn(mDevicePolicyManagerInternal); + when(mWifiPermissionsUtil.isDeviceOwner(anyInt(), any())).thenReturn(false); + when(mWifiPermissionsUtil.isProfileOwner(anyInt(), any())).thenReturn(false); when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog); when(mWifiInjector.getWifiLastResortWatchdog().shouldIgnoreSsidUpdate()) .thenReturn(false); when(mWifiInjector.getCarrierNetworkConfig()).thenReturn(mCarrierNetworkConfig); createWifiConfigManager(); - mWifiConfigManager.setOnSavedNetworkUpdateListener(mWcmListener); + mWifiConfigManager.addOnNetworkUpdateListener(mWcmListener); ArgumentCaptor<ContentObserver> observerCaptor = ArgumentCaptor.forClass(ContentObserver.class); verify(mFrameworkFacade).registerContentObserver(eq(mContext), eq(Settings.Global.getUriFor( @@ -228,9 +230,13 @@ public class WifiConfigManagerTest { // static mocking mSession = ExtendedMockito.mockitoSession() .mockStatic(WifiConfigStore.class, withSettings().lenient()) + .spyStatic(WifiConfigurationUtil.class) + .strictness(Strictness.LENIENT) .startMocking(); when(WifiConfigStore.createUserFiles(anyInt())).thenReturn(mock(List.class)); when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager); + when(WifiConfigurationUtil.calculatePersistentMacForConfiguration(any(), any())) + .thenReturn(TEST_RANDOMIZED_MAC); } /** @@ -239,7 +245,9 @@ public class WifiConfigManagerTest { @After public void cleanup() { validateMockitoUsage(); - mSession.finishMocking(); + if (mSession != null) { + mSession.finishMocking(); + } } /** @@ -368,6 +376,7 @@ public class WifiConfigManagerTest { */ @Test public void testAddingNetworkWithMatchingMacAddressOverridesField() { + int prevMappingSize = mWifiConfigManager.getRandomizedMacAddressMappingSize(); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); Map<String, String> randomizedMacAddressMapping = new HashMap<>(); final String randMac = "12:23:34:45:56:67"; @@ -382,6 +391,9 @@ public class WifiConfigManagerTest { List<WifiConfiguration> retrievedNetworks = mWifiConfigManager.getConfiguredNetworksWithPasswords(); assertEquals(randMac, retrievedNetworks.get(0).getRandomizedMacAddress().toString()); + // Verify that for networks that we already have randomizedMacAddressMapping saved + // we are still correctly writing into the WifiConfigStore. + assertEquals(prevMappingSize + 1, mWifiConfigManager.getRandomizedMacAddressMappingSize()); } /** @@ -391,6 +403,7 @@ public class WifiConfigManagerTest { */ @Test public void testRandomizedMacAddressIsPersistedOverForgetNetwork() { + int prevMappingSize = mWifiConfigManager.getRandomizedMacAddressMappingSize(); // Create and add an open network WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); verifyAddNetworkToWifiConfigManager(openNetwork); @@ -410,6 +423,8 @@ public class WifiConfigManagerTest { verifyAddNetworkToWifiConfigManager(openNetwork); retrievedNetworks = mWifiConfigManager.getConfiguredNetworksWithPasswords(); assertEquals(randMac, retrievedNetworks.get(0).getRandomizedMacAddress().toString()); + // Verify that we are no longer persisting the randomized MAC address with WifiConfigStore. + assertEquals(prevMappingSize, mWifiConfigManager.getRandomizedMacAddressMappingSize()); } /** @@ -438,7 +453,8 @@ public class WifiConfigManagerTest { // This MAC should be different from the default, uninitialized randomized MAC. assertNotEquals(defaultMac, randMac); - assertTrue(mWifiConfigManager.removeNetwork(openNetwork.networkId, TEST_CREATOR_UID)); + assertTrue(mWifiConfigManager.removeNetwork( + openNetwork.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty()); // Adds the network back again and verify randomized MAC address stays the same. @@ -453,12 +469,15 @@ public class WifiConfigManagerTest { */ @Test public void testUpdateSingleOpenNetwork() { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); List<WifiConfiguration> networks = new ArrayList<>(); networks.add(openNetwork); verifyAddNetworkToWifiConfigManager(openNetwork); - verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); reset(mWcmListener); // Now change BSSID for the network. @@ -470,7 +489,8 @@ public class WifiConfigManagerTest { mWifiConfigManager.getConfiguredNetworksWithPasswords(); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( networks, retrievedNetworks); - verify(mWcmListener).onSavedNetworkUpdated(openNetwork.networkId); + verify(mWcmListener).onNetworkUpdated(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -479,15 +499,17 @@ public class WifiConfigManagerTest { */ @Test public void testCannotUpdateMacRandomizationSettingWithoutPermission() { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false); - when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(), anyInt())) - .thenReturn(true); + when(mWifiPermissionsUtil.isDeviceOwner(anyInt(), any())).thenReturn(true); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); openNetwork.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; verifyAddNetworkToWifiConfigManager(openNetwork); - verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); reset(mWcmListener); // Change BSSID for the network and verify success @@ -507,6 +529,8 @@ public class WifiConfigManagerTest { */ @Test public void testCanUpdateMacRandomizationSettingWithNetworkSettingPermission() { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); openNetwork.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; @@ -514,7 +538,8 @@ public class WifiConfigManagerTest { networks.add(openNetwork); verifyAddNetworkToWifiConfigManager(openNetwork); - verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); reset(mWcmListener); // Now change the macRandomizationSetting and verify success @@ -534,17 +559,19 @@ public class WifiConfigManagerTest { */ @Test public void testCanUpdateMacRandomizationSettingWithSetupWizardPermission() { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(true); - when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(), anyInt())) - .thenReturn(true); + when(mWifiPermissionsUtil.isDeviceOwner(anyInt(), any())).thenReturn(true); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); openNetwork.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; List<WifiConfiguration> networks = new ArrayList<>(); networks.add(openNetwork); verifyAddNetworkToWifiConfigManager(openNetwork); - verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); reset(mWcmListener); // Now change the macRandomizationSetting and verify success @@ -566,6 +593,8 @@ public class WifiConfigManagerTest { */ @Test public void testAddSingleEphemeralNetwork() throws Exception { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createOpenNetwork(); ephemeralNetwork.ephemeral = true; List<WifiConfiguration> networks = new ArrayList<>(); @@ -580,7 +609,8 @@ public class WifiConfigManagerTest { // Ensure that this is not returned in the saved network list. assertTrue(mWifiConfigManager.getSavedNetworks(Process.WIFI_UID).isEmpty()); - verify(mWcmListener, never()).onSavedNetworkAdded(ephemeralNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(ephemeralNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -590,6 +620,8 @@ public class WifiConfigManagerTest { */ @Test public void testAddSinglePasspointNetwork() throws Exception { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork(); verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork); @@ -598,7 +630,8 @@ public class WifiConfigManagerTest { // Ensure that this is not returned in the saved network list. assertTrue(mWifiConfigManager.getSavedNetworks(Process.WIFI_UID).isEmpty()); - verify(mWcmListener, never()).onSavedNetworkAdded(passpointNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(passpointNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -633,6 +666,8 @@ public class WifiConfigManagerTest { @Test public void testAddSingleSuggestionNetwork() throws Exception { WifiConfiguration suggestionNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); suggestionNetwork.ephemeral = true; suggestionNetwork.fromWifiNetworkSuggestion = true; List<WifiConfiguration> networks = new ArrayList<>(); @@ -647,7 +682,8 @@ public class WifiConfigManagerTest { // Ensure that this is not returned in the saved network list. assertTrue(mWifiConfigManager.getSavedNetworks(Process.WIFI_UID).isEmpty()); - verify(mWcmListener, never()).onSavedNetworkAdded(suggestionNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(suggestionNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -657,6 +693,8 @@ public class WifiConfigManagerTest { */ @Test public void testAddSingleSpecifierNetwork() throws Exception { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration suggestionNetwork = WifiConfigurationTestUtil.createOpenNetwork(); suggestionNetwork.ephemeral = true; suggestionNetwork.fromWifiNetworkSpecifier = true; @@ -672,7 +710,8 @@ public class WifiConfigManagerTest { // Ensure that this is not returned in the saved network list. assertTrue(mWifiConfigManager.getSavedNetworks(Process.WIFI_UID).isEmpty()); - verify(mWcmListener, never()).onSavedNetworkAdded(suggestionNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(suggestionNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -797,8 +836,7 @@ public class WifiConfigManagerTest { // 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); + when(mWifiPermissionsUtil.isProfileOwner(anyInt(), any())).thenReturn(true); // Change the IpConfiguration now and ensure that the IP configuration flags are set now. assertAndSetNetworkIpConfiguration( @@ -819,10 +857,13 @@ public class WifiConfigManagerTest { */ @Test public void testRemoveSingleOpenNetwork() { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); verifyAddNetworkToWifiConfigManager(openNetwork); - verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); reset(mWcmListener); // Ensure that configured network list is not empty. @@ -831,7 +872,8 @@ public class WifiConfigManagerTest { verifyRemoveNetworkFromWifiConfigManager(openNetwork); // Ensure that configured network list is empty now. assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener).onSavedNetworkRemoved(openNetwork.networkId); + verify(mWcmListener).onNetworkRemoved(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -842,16 +884,19 @@ public class WifiConfigManagerTest { public void testRemoveSingleEphemeralNetwork() throws Exception { WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createOpenNetwork(); ephemeralNetwork.ephemeral = true; + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); verifyAddEphemeralNetworkToWifiConfigManager(ephemeralNetwork); // Ensure that configured network list is not empty. assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener, never()).onSavedNetworkAdded(ephemeralNetwork.networkId); - + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(ephemeralNetwork.networkId, wifiConfigCaptor.getValue().networkId); verifyRemoveEphemeralNetworkFromWifiConfigManager(ephemeralNetwork); // Ensure that configured network list is empty now. assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener, never()).onSavedNetworkRemoved(ephemeralNetwork.networkId); + verify(mWcmListener).onNetworkRemoved(wifiConfigCaptor.capture()); + assertEquals(ephemeralNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -860,17 +905,21 @@ public class WifiConfigManagerTest { */ @Test public void testRemoveSinglePasspointNetwork() throws Exception { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork(); verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork); // Ensure that configured network list is not empty. assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener, never()).onSavedNetworkAdded(passpointNetwork.networkId); + verify(mWcmListener).onNetworkAdded(wifiConfigCaptor.capture()); + assertEquals(passpointNetwork.networkId, wifiConfigCaptor.getValue().networkId); verifyRemovePasspointNetworkFromWifiConfigManager(passpointNetwork); // Ensure that configured network list is empty now. assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener, never()).onSavedNetworkRemoved(passpointNetwork.networkId); + verify(mWcmListener).onNetworkRemoved(wifiConfigCaptor.capture()); + assertEquals(passpointNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -888,7 +937,8 @@ public class WifiConfigManagerTest { // Ensure that configured network list is not empty. assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - assertTrue(mWifiConfigManager.removeNetwork(passpointNetwork.networkId, Process.WIFI_UID)); + assertTrue(mWifiConfigManager.removeNetwork( + passpointNetwork.networkId, Process.WIFI_UID, null)); // Verify keys are not being removed. verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class)); @@ -932,8 +982,7 @@ public class WifiConfigManagerTest { // 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); + when(mWifiPermissionsUtil.isProfileOwner(anyInt(), any())).thenReturn(true); verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(openNetwork); verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(pskNetwork); @@ -958,6 +1007,8 @@ public class WifiConfigManagerTest { */ @Test public void testNetworkSelectionStatus() { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork); @@ -975,19 +1026,21 @@ public class WifiConfigManagerTest { for (int i = 1; i <= assocRejectThreshold; i++) { verifyUpdateNetworkSelectionStatus(result.getNetworkId(), assocRejectReason, i); } - verify(mWcmListener).onSavedNetworkTemporarilyDisabled( - networkId, NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION); - + verify(mWcmListener).onNetworkTemporarilyDisabled(wifiConfigCaptor.capture(), + eq(NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION)); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); // Now set it to permanently disabled. verifyUpdateNetworkSelectionStatus( result.getNetworkId(), NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER, 0); - verify(mWcmListener).onSavedNetworkPermanentlyDisabled( - networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); - + verify(mWcmListener).onNetworkPermanentlyDisabled( + wifiConfigCaptor.capture(), eq(NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); // Now set it back to enabled. verifyUpdateNetworkSelectionStatus( result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0); - verify(mWcmListener, times(2)).onSavedNetworkEnabled(networkId); + verify(mWcmListener, times(2)) + .onNetworkEnabled(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -996,6 +1049,8 @@ public class WifiConfigManagerTest { */ @Test public void testNetworkSelectionStatusTemporarilyDisabledDueToNoInternet() { + ArgumentCaptor<WifiConfiguration> wifiConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork); @@ -1007,15 +1062,18 @@ public class WifiConfigManagerTest { // Now set it to temporarily disabled. The threshold for no internet is 1, so // disable it once to actually mark it temporarily disabled. - verifyUpdateNetworkSelectionStatus( - result.getNetworkId(), NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY, 1); - verify(mWcmListener).onSavedNetworkTemporarilyDisabled( - networkId, NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY); - + verifyUpdateNetworkSelectionStatus(result.getNetworkId(), + NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY, 1); + verify(mWcmListener).onNetworkTemporarilyDisabled( + wifiConfigCaptor.capture(), + eq(NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY)); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); // Now set it back to enabled. verifyUpdateNetworkSelectionStatus( result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0); - verify(mWcmListener, times(2)).onSavedNetworkEnabled(networkId); + verify(mWcmListener, times(2)) + .onNetworkEnabled(wifiConfigCaptor.capture()); + assertEquals(openNetwork.networkId, wifiConfigCaptor.getValue().networkId); } /** @@ -1143,7 +1201,7 @@ public class WifiConfigManagerTest { NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork); assertTrue(mWifiConfigManager.enableNetwork( - result.getNetworkId(), false, TEST_CREATOR_UID)); + result.getNetworkId(), false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus(); @@ -1152,7 +1210,8 @@ public class WifiConfigManagerTest { mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true)); // Now set it disabled. - assertTrue(mWifiConfigManager.disableNetwork(result.getNetworkId(), TEST_CREATOR_UID)); + assertTrue(mWifiConfigManager.disableNetwork( + result.getNetworkId(), TEST_CREATOR_UID, TEST_CREATOR_NAME)); retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); retrievedStatus = retrievedNetwork.getNetworkSelectionStatus(); assertTrue(retrievedStatus.isNetworkPermanentlyDisabled()); @@ -1178,7 +1237,7 @@ public class WifiConfigManagerTest { // Now try to set it enable with |TEST_UPDATE_UID|, it should fail and the network // should remain disabled. assertFalse(mWifiConfigManager.enableNetwork( - result.getNetworkId(), true, TEST_UPDATE_UID)); + result.getNetworkId(), true, TEST_UPDATE_UID, TEST_UPDATE_NAME)); WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus(); @@ -1201,13 +1260,14 @@ public class WifiConfigManagerTest { WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork); assertTrue(mWifiConfigManager.enableNetwork( - result.getNetworkId(), true, TEST_CREATOR_UID)); + result.getNetworkId(), true, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertEquals(result.getNetworkId(), mWifiConfigManager.getLastSelectedNetwork()); // 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)); + assertFalse(mWifiConfigManager.disableNetwork( + result.getNetworkId(), TEST_UPDATE_UID, TEST_CREATOR_NAME)); WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus(); @@ -1220,6 +1280,31 @@ public class WifiConfigManagerTest { } /** + * Verifies the allowance/disallowance of autojoin to a network using + * {@link WifiConfigManager.allowAutojoin(int, boolean)} + */ + @Test + public void testAllowDisallowAutojoin() throws Exception { + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + + NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork); + + assertTrue(mWifiConfigManager.allowAutojoin( + result.getNetworkId(), true)); + WifiConfiguration retrievedNetwork = + mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); + assertTrue(retrievedNetwork.allowAutojoin); + mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true)); + + // Now set it disallow auto-join. + assertTrue(mWifiConfigManager.allowAutojoin( + result.getNetworkId(), false)); + retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); + assertFalse(retrievedNetwork.allowAutojoin); + mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true)); + } + + /** * Verifies the updation of network's connectUid using * {@link WifiConfigManager#updateLastConnectUid(int, int)}. */ @@ -1257,7 +1342,8 @@ public class WifiConfigManagerTest { @Test public void testRemoveNetworkWithEmptyConfigStore() { int networkId = new Random().nextInt(); - assertFalse(mWifiConfigManager.removeNetwork(networkId, TEST_CREATOR_UID)); + assertFalse(mWifiConfigManager.removeNetwork( + networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); } /** @@ -1273,7 +1359,8 @@ public class WifiConfigManagerTest { // Change the networkID to an invalid one. openNetwork.networkId++; - assertFalse(mWifiConfigManager.removeNetwork(openNetwork.networkId, TEST_CREATOR_UID)); + assertFalse(mWifiConfigManager.removeNetwork( + openNetwork.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); } /** @@ -1291,8 +1378,9 @@ public class WifiConfigManagerTest { NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork); assertFalse(mWifiConfigManager.enableNetwork( - result.getNetworkId() + 1, false, TEST_CREATOR_UID)); - assertFalse(mWifiConfigManager.disableNetwork(result.getNetworkId() + 1, TEST_CREATOR_UID)); + result.getNetworkId() + 1, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertFalse(mWifiConfigManager.disableNetwork( + result.getNetworkId() + 1, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertFalse(mWifiConfigManager.updateNetworkSelectionStatus( result.getNetworkId() + 1, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)); assertFalse(mWifiConfigManager.updateLastConnectUid( @@ -1901,9 +1989,6 @@ public class WifiConfigManagerTest { WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(config); - MacAddress testMac = MacAddress.createRandomUnicastAddress(); - mWifiConfigManager.setNetworkRandomizedMacAddress(result.getNetworkId(), testMac); - // Verify that randomized MAC address is masked when obtaining saved networks from // invalid UID List<WifiConfiguration> configs = mWifiConfigManager.getSavedNetworks(Process.INVALID_UID); @@ -1914,7 +1999,8 @@ public class WifiConfigManagerTest { // system UID configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID); assertEquals(1, configs.size()); - assertEquals(testMac, configs.get(0).getRandomizedMacAddress()); + String macAddress = configs.get(0).getRandomizedMacAddress().toString(); + assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, macAddress); // Verify that randomized MAC address is masked when obtaining saved networks from // (carrier app) non-creator of the config @@ -1926,18 +2012,149 @@ public class WifiConfigManagerTest { // (carrier app) creator of the config configs = mWifiConfigManager.getSavedNetworks(TEST_CREATOR_UID); assertEquals(1, configs.size()); - assertEquals(testMac, configs.get(0).getRandomizedMacAddress()); + assertEquals(macAddress, configs.get(0).getRandomizedMacAddress().toString()); // Verify that randomized MAC address is unmasked when getting list of privileged (with // password) configurations WifiConfiguration configWithRandomizedMac = mWifiConfigManager .getConfiguredNetworkWithPassword(result.getNetworkId()); - assertEquals(testMac, configWithRandomizedMac.getRandomizedMacAddress()); + assertEquals(macAddress, configs.get(0).getRandomizedMacAddress().toString()); // Ensure that the MAC address is present when asked for config with MAC address. configWithRandomizedMac = mWifiConfigManager .getConfiguredNetworkWithoutMasking(result.getNetworkId()); - assertEquals(testMac, configWithRandomizedMac.getRandomizedMacAddress()); + assertEquals(macAddress, configs.get(0).getRandomizedMacAddress().toString()); + } + + /** + * Verifies that getRandomizedMacAndUpdateIfNeeded updates the randomized MAC address and + * |randomizedMacLastModifiedTimeMs| correctly. + * + * Then verify that getRandomizedMacAndUpdateIfNeeded sets the randomized MAC back to the + * persistent MAC. + */ + @Test + public void testRandomizedMacUpdateAndRestore() { + setUpWifiConfigurationForAggressiveRandomization(true); + // get the persistent randomized MAC address. + WifiConfiguration config = getFirstInternalWifiConfiguration(); + final String persistentMacString = config.getRandomizedMacAddress().toString(); + assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, persistentMacString); + + // verify the new randomized mac should be different from the original mac. + when(mClock.getWallClockMillis()).thenReturn( + WifiConfigManager.AGGRESSIVE_MAC_REFRESH_MS + 1); + MacAddress newMac = mWifiConfigManager.getRandomizedMacAndUpdateIfNeeded(config); + + // verify internal WifiConfiguration has MacAddress updated correctly by comparing the + // MAC address from internal WifiConfiguration with the value returned by API. + config = getFirstInternalWifiConfiguration(); + assertEquals(newMac, config.getRandomizedMacAddress()); + assertNotEquals(persistentMacString, newMac.toString()); + assertEquals(WifiConfigManager.AGGRESSIVE_MAC_REFRESH_MS + 1, + config.randomizedMacLastModifiedTimeMs); + + // Now disable aggressive randomization and verify the randomized MAC is changed back to + // the persistent MAC. + Set<String> blacklist = new HashSet<>(); + blacklist.add(config.SSID); + mWifiConfigManager.setAggressiveMacRandomizationBlacklist(blacklist); + + // verify the randomized mac should be set back to the persistent mac. + when(mClock.getWallClockMillis()).thenReturn( + WifiConfigManager.AGGRESSIVE_MAC_REFRESH_MS * 2); + newMac = mWifiConfigManager.getRandomizedMacAndUpdateIfNeeded(config); + + // verify internal WifiConfiguration has MacAddress updated correctly by comparing the + // MAC address from internal WifiConfiguration with the value returned by API. + config = getFirstInternalWifiConfiguration(); + assertEquals(newMac, config.getRandomizedMacAddress()); + assertEquals(persistentMacString, newMac.toString()); + assertEquals(WifiConfigManager.AGGRESSIVE_MAC_REFRESH_MS * 2, + config.randomizedMacLastModifiedTimeMs); + } + + /** + * Verifies that the randomized MAC address is not updated when insufficient time have past + * since the previous update. + */ + @Test + public void testRandomizedMacIsNotUpdatedDueToTimeConstraint() { + setUpWifiConfigurationForAggressiveRandomization(true); + // get the persistent randomized MAC address. + WifiConfiguration config = getFirstInternalWifiConfiguration(); + final String persistentMacString = config.getRandomizedMacAddress().toString(); + assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, persistentMacString); + + // verify that the randomized MAC is unchanged. + when(mClock.getWallClockMillis()).thenReturn( + WifiConfigManager.AGGRESSIVE_MAC_REFRESH_MS); + MacAddress newMac = mWifiConfigManager.getRandomizedMacAndUpdateIfNeeded(config); + assertEquals(persistentMacString, newMac.toString()); + } + + /** + * Verifies that the randomized MAC address is not updated when if the network has not been + * connected to before. + */ + @Test + public void testRandomizedMacIsNotUpdatedDueToHasNotConnected() { + setUpWifiConfigurationForAggressiveRandomization(false); + // get the persistent randomized MAC address. + WifiConfiguration config = getFirstInternalWifiConfiguration(); + final String persistentMacString = config.getRandomizedMacAddress().toString(); + assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, persistentMacString); + + // verify that the randomized MAC is unchanged. + when(mClock.getWallClockMillis()).thenReturn( + WifiConfigManager.AGGRESSIVE_MAC_REFRESH_MS + 1); + MacAddress newMac = mWifiConfigManager.getRandomizedMacAndUpdateIfNeeded(config); + assertEquals(persistentMacString, newMac.toString()); + } + + /** + * Verifies that the randomized MAC address is not updated when the aggressive randomization + * whitelist feature flag is disabled. + */ + @Test + public void testRandomizedMacIsNotUpdatedDueToFeatureDisabled() { + setUpWifiConfigurationForAggressiveRandomization(true); + // get the persistent randomized MAC address. + WifiConfiguration config = getFirstInternalWifiConfiguration(); + final String persistentMacString = config.getRandomizedMacAddress().toString(); + assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, persistentMacString); + + // disable the feature flag here. + when(mDeviceConfigFacade.isAggressiveMacRandomizationSsidWhitelistEnabled()) + .thenReturn(false); + + // verify that the randomized MAC is unchanged. + when(mClock.getWallClockMillis()).thenReturn( + WifiConfigManager.AGGRESSIVE_MAC_REFRESH_MS + 1); + MacAddress newMac = mWifiConfigManager.getRandomizedMacAndUpdateIfNeeded(config); + assertEquals(persistentMacString, newMac.toString()); + } + + private WifiConfiguration getFirstInternalWifiConfiguration() { + List<WifiConfiguration> configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID); + assertEquals(1, configs.size()); + return configs.get(0); + } + + private void setUpWifiConfigurationForAggressiveRandomization(boolean hasEverConnected) { + // sets up a WifiConfiguration for aggressive randomization. + when(mDeviceConfigFacade.isAggressiveMacRandomizationSsidWhitelistEnabled()) + .thenReturn(true); + WifiConfiguration c = WifiConfigurationTestUtil.createOpenNetwork(); + NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(c); + // Adds the WifiConfiguration to aggressive randomization whitelist. + Set<String> ssidList = new HashSet<>(); + ssidList.add(c.SSID); + mWifiConfigManager.setAggressiveMacRandomizationWhitelist(ssidList); + if (hasEverConnected) { + // sets hasEverConnected to true. + mWifiConfigManager.updateNetworkAfterConnect(c.networkId); + } } /** @@ -1948,9 +2165,6 @@ public class WifiConfigManagerTest { WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(config); - MacAddress testMac = MacAddress.createRandomUnicastAddress(); - mWifiConfigManager.setNetworkRandomizedMacAddress(result.getNetworkId(), testMac); - // Verify macRandomizationSetting is not masked out when feature is supported. List<WifiConfiguration> configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID); assertEquals(1, configs.size()); @@ -1969,9 +2183,6 @@ public class WifiConfigManagerTest { WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(config); - MacAddress testMac = MacAddress.createRandomUnicastAddress(); - mWifiConfigManager.setNetworkRandomizedMacAddress(result.getNetworkId(), testMac); - // Verify macRandomizationSetting is masked out when feature is unsupported. List<WifiConfiguration> configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID); assertEquals(1, configs.size()); @@ -2028,9 +2239,12 @@ public class WifiConfigManagerTest { 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)); + assertTrue(mWifiConfigManager.enableNetwork( + network1.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network2.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network3.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); // Now set scan results in 2 of them to set the corresponding // {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} field. @@ -2052,7 +2266,8 @@ public class WifiConfigManagerTest { 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)); + assertTrue(mWifiConfigManager.disableNetwork( + network3.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); // Retrieve the Pno network list again & verify the order of the networks returned. pnoNetworks = mWifiConfigManager.retrievePnoNetworkList(); @@ -2078,8 +2293,10 @@ public class WifiConfigManagerTest { verifyAddNetworkToWifiConfigManager(network2); // 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( + network1.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network2.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertTrue(mWifiConfigManager.updateNetworkAfterConnect(network1.networkId)); // Retrieve the Pno network list & verify the order of the networks returned. @@ -2138,7 +2355,8 @@ public class WifiConfigManagerTest { mContentObserverPnoChannelCulling.onChange(false); WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(); verifyAddNetworkToWifiConfigManager(network1); - assertTrue(mWifiConfigManager.enableNetwork(network1.networkId, false, TEST_CREATOR_UID)); + assertTrue(mWifiConfigManager.enableNetwork( + network1.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertTrue(mWifiConfigManager.updateNetworkAfterConnect(network1.networkId)); ScanDetail scanDetail1 = createScanDetailForNetwork(network1, TEST_BSSID + "1", TEST_RSSI, TEST_FREQUENCY_1); @@ -2169,9 +2387,12 @@ public class WifiConfigManagerTest { 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)); + assertTrue(mWifiConfigManager.enableNetwork( + network1.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network2.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network3.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); long firstConnectionTimeMillis = 45677; long secondConnectionTimeMillis = firstConnectionTimeMillis + 45; @@ -2212,9 +2433,12 @@ public class WifiConfigManagerTest { 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)); + assertTrue(mWifiConfigManager.enableNetwork( + network1.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network2.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network3.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); long firstConnectionTimeMillis = 45677; long secondConnectionTimeMillis = firstConnectionTimeMillis + 45; @@ -2269,9 +2493,12 @@ public class WifiConfigManagerTest { 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)); + assertTrue(mWifiConfigManager.enableNetwork( + network1.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network2.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); + assertTrue(mWifiConfigManager.enableNetwork( + network3.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); long firstConnectionTimeMillis = 45677; long secondConnectionTimeMillis = firstConnectionTimeMillis + 45; @@ -2312,11 +2539,11 @@ public class WifiConfigManagerTest { // Enable all of them. assertTrue(mWifiConfigManager.enableNetwork( - savedOpenNetwork.networkId, false, TEST_CREATOR_UID)); + savedOpenNetwork.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertTrue(mWifiConfigManager.enableNetwork( - ephemeralNetwork.networkId, false, TEST_CREATOR_UID)); + ephemeralNetwork.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertTrue(mWifiConfigManager.enableNetwork( - passpointNetwork.networkId, false, TEST_CREATOR_UID)); + passpointNetwork.networkId, false, TEST_CREATOR_UID, TEST_CREATOR_NAME)); // Retrieve the Pno network list & verify the order of the networks returned. List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks = @@ -2886,7 +3113,7 @@ public class WifiConfigManagerTest { }; setupStoreDataForUserRead(user2Networks, new HashMap<>()); // Now switch the user to user 2 and ensure that shared network's IDs have not changed. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); verify(mWifiConfigStore).switchUserStoresAndRead(any(List.class)); @@ -2964,7 +3191,7 @@ public class WifiConfigManagerTest { }; setupStoreDataForUserRead(user2Networks, new HashMap<>()); // Now switch the user to user 2 and ensure that user 1's private network has been removed. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(true); Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2); verify(mWifiConfigStore).switchUserStoresAndRead(any(List.class)); assertTrue((removedNetworks.size() == 1) && (removedNetworks.contains(user1NetworkId))); @@ -2981,7 +3208,7 @@ public class WifiConfigManagerTest { // 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); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(true); removedNetworks = mWifiConfigManager.handleUserSwitch(user2); assertTrue(removedNetworks.isEmpty()); } @@ -3025,7 +3252,7 @@ public class WifiConfigManagerTest { }; setupStoreDataForUserRead(user2Networks, new HashMap<>()); // Now switch the user to user 2 and ensure that no private network has been removed. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(true); Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2); verify(mWifiConfigStore).switchUserStoresAndRead(any(List.class)); assertTrue(removedNetworks.isEmpty()); @@ -3089,7 +3316,7 @@ public class WifiConfigManagerTest { // 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); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(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 @@ -3179,7 +3406,7 @@ public class WifiConfigManagerTest { setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>()); // user2 is unlocked and switched to foreground. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); // Ensure that the read was invoked. mContextConfigStoreMockOrder.verify(mWifiConfigStore) @@ -3201,7 +3428,7 @@ public class WifiConfigManagerTest { assertTrue(mWifiConfigManager.loadFromStore()); // user2 is locked and switched to foreground. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(false); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(false); mWifiConfigManager.handleUserSwitch(user2); // Ensure that the read was not invoked. @@ -3234,7 +3461,7 @@ public class WifiConfigManagerTest { assertTrue(mWifiConfigManager.loadFromStore()); // Try stopping background user2 first, this should not do anything. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(false); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(false); mWifiConfigManager.handleUserStop(user2); mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()) .switchUserStoresAndRead(any(List.class)); @@ -3458,7 +3685,7 @@ public class WifiConfigManagerTest { setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>()); // user2 is unlocked and switched to foreground. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); // Ensure that the read was invoked. mContextConfigStoreMockOrder.verify(mWifiConfigStore) @@ -3499,7 +3726,7 @@ public class WifiConfigManagerTest { int user2 = TEST_DEFAULT_USER + 1; setupUserProfiles(user2); - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(false); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(false); mWifiConfigManager.handleUserSwitch(user2); // Create a network for user2 try adding it. This should be rejected. @@ -3560,7 +3787,7 @@ public class WifiConfigManagerTest { setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>()); // Now switch the user to user 2. - when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(user2))).thenReturn(true); mWifiConfigManager.handleUserSwitch(user2); // Ensure that the read was invoked. mContextConfigStoreMockOrder.verify(mWifiConfigStore) @@ -3580,22 +3807,24 @@ public class WifiConfigManagerTest { when(mClock.getElapsedSinceBootMillis()).thenReturn(67L); assertTrue(mWifiConfigManager.enableNetwork( - result.getNetworkId(), true, TEST_CREATOR_UID)); + result.getNetworkId(), true, TEST_CREATOR_UID, TEST_CREATOR_NAME)); 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)); + assertTrue(mWifiConfigManager.disableNetwork( + result.getNetworkId(), TEST_CREATOR_UID, TEST_CREATOR_NAME)); 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)); + result.getNetworkId(), true, TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertEquals(result.getNetworkId(), mWifiConfigManager.getLastSelectedNetwork()); assertEquals(openNetwork.configKey(), mWifiConfigManager.getLastSelectedNetworkConfigKey()); - assertTrue(mWifiConfigManager.removeNetwork(result.getNetworkId(), TEST_CREATOR_UID)); + assertTrue(mWifiConfigManager.removeNetwork( + result.getNetworkId(), TEST_CREATOR_UID, TEST_CREATOR_NAME)); assertEquals( WifiConfiguration.INVALID_NETWORK_ID, mWifiConfigManager.getLastSelectedNetwork()); } @@ -3666,14 +3895,16 @@ public class WifiConfigManagerTest { 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)); + assertTrue(mWifiConfigManager.removeNetwork( + network3.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); 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)); + assertTrue(mWifiConfigManager.removeNetwork( + network2.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(network1.networkId); assertNotEquals( network2.configKey(), @@ -3719,7 +3950,7 @@ public class WifiConfigManagerTest { } /** - * Verifies that Passpoint network corresponding with given FQDN is removed. + * Verifies that Passpoint network corresponding with given config key (FQDN) is removed. * * @throws Exception */ @@ -3729,7 +3960,22 @@ public class WifiConfigManagerTest { verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork); assertTrue(mWifiConfigManager.removePasspointConfiguredNetwork( - WifiConfigurationTestUtil.TEST_FQDN)); + passpointNetwork.configKey())); + } + + /** + * Verifies that suggested network corresponding with given config key is removed. + * + * @throws Exception + */ + @Test + public void testRemoveSuggestionConfiguredNetwork() throws Exception { + WifiConfiguration suggestedNetwork = WifiConfigurationTestUtil.createEphemeralNetwork(); + suggestedNetwork.fromWifiNetworkSuggestion = true; + verifyAddEphemeralNetworkToWifiConfigManager(suggestedNetwork); + + assertTrue(mWifiConfigManager.removeSuggestionConfiguredNetwork( + suggestedNetwork.configKey())); } /** @@ -4298,31 +4544,6 @@ public class WifiConfigManagerTest { } /** - * Verifies that the method setNetworkRandomizedMacAddress changes the randomized MAC - * address variable in the internal configuration. - */ - @Test - public void testSetNetworkRandomizedMacAddressUpdatesInternalMacAddress() { - WifiConfiguration originalConfig = WifiConfigurationTestUtil.createOpenNetwork(); - NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(originalConfig); - - // Verify that internal randomized MAC address does not change from - // from setting external randomized MAC address - MacAddress originalMac = originalConfig.getOrCreateRandomizedMacAddress(); - WifiConfiguration retrievedConfig = mWifiConfigManager - .getConfiguredNetworkWithoutMasking(result.getNetworkId()); - assertNotEquals(originalMac, retrievedConfig.getRandomizedMacAddress()); - - // Verify that changing randomized MAC address through setNetworkRandomizedMacAddress - // changes the internal randomized MAC address - MacAddress newMac = MacAddress.createRandomUnicastAddress(); - mWifiConfigManager.setNetworkRandomizedMacAddress(result.getNetworkId(), newMac); - retrievedConfig = mWifiConfigManager - .getConfiguredNetworkWithoutMasking(result.getNetworkId()); - assertEquals(newMac, retrievedConfig.getRandomizedMacAddress()); - } - - /** * Verifies that the method resetSimNetworks updates SIM presence status and SIM configs. */ @Test @@ -4544,11 +4765,15 @@ public class WifiConfigManagerTest { currentTimeMs = disableTimeMs + WifiConfigManager.DELETED_EPHEMERAL_SSID_EXPIRY_MS - 1; when(mClock.getWallClockMillis()).thenReturn(currentTimeMs); assertTrue(mWifiConfigManager.wasEphemeralNetworkDeleted(config.SSID)); + assertTrue(mWifiConfigManager.getConfiguredNetwork(config.networkId) + .getNetworkSelectionStatus().isNetworkPermanentlyDisabled()); // After the expiry of timeout. currentTimeMs = disableTimeMs + WifiConfigManager.DELETED_EPHEMERAL_SSID_EXPIRY_MS + 1; when(mClock.getWallClockMillis()).thenReturn(currentTimeMs); assertFalse(mWifiConfigManager.wasEphemeralNetworkDeleted(config.SSID)); + assertFalse(mWifiConfigManager.getConfiguredNetwork(config.networkId) + .getNetworkSelectionStatus().isNetworkPermanentlyDisabled()); } private NetworkUpdateResult verifyAddOrUpdateNetworkWithProxySettingsAndPermissions( @@ -4578,12 +4803,10 @@ public class WifiConfigManagerTest { 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))) + when(mWifiPermissionsUtil.isDeviceOwner(anyInt(), any())) .thenReturn(withDeviceOwnerPolicy); + when(mWifiPermissionsUtil.isProfileOwner(anyInt(), any())) + .thenReturn(withProfileOwnerPolicy); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())) .thenReturn(withNetworkSettings); when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())) @@ -4604,7 +4827,7 @@ public class WifiConfigManagerTest { mWifiPermissionsUtil, mWifiPermissionsWrapper, mWifiInjector, mNetworkListSharedStoreData, mNetworkListUserStoreData, mDeletedEphemeralSsidsStoreData, mRandomizedMacStoreData, - mFrameworkFacade, mLooper.getLooper()); + mFrameworkFacade, new Handler(mLooper.getLooper()), mDeviceConfigFacade); mWifiConfigManager.enableVerboseLogging(1); } @@ -5073,7 +5296,8 @@ public class WifiConfigManagerTest { */ private void verifyRemoveNetworkFromWifiConfigManager( WifiConfiguration configuration) { - assertTrue(mWifiConfigManager.removeNetwork(configuration.networkId, TEST_CREATOR_UID)); + assertTrue(mWifiConfigManager.removeNetwork( + configuration.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); verifyNetworkRemoveBroadcast(configuration); // Verify if the config store write was triggered without this new configuration. @@ -5085,7 +5309,8 @@ public class WifiConfigManagerTest { */ private void verifyRemoveEphemeralNetworkFromWifiConfigManager( WifiConfiguration configuration) throws Exception { - assertTrue(mWifiConfigManager.removeNetwork(configuration.networkId, TEST_CREATOR_UID)); + assertTrue(mWifiConfigManager.removeNetwork( + configuration.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); verifyNetworkRemoveBroadcast(configuration); // Ensure that the write was not invoked for ephemeral network remove. @@ -5097,7 +5322,8 @@ public class WifiConfigManagerTest { */ private void verifyRemovePasspointNetworkFromWifiConfigManager( WifiConfiguration configuration) throws Exception { - assertTrue(mWifiConfigManager.removeNetwork(configuration.networkId, TEST_CREATOR_UID)); + assertTrue(mWifiConfigManager.removeNetwork( + configuration.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME)); // Verify keys are not being removed. verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class)); @@ -5279,11 +5505,7 @@ public class WifiConfigManagerTest { * @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); + when(mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(userId))).thenReturn(true); } private void verifyRemoveNetworksForApp() { diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java index b59e367dd2..799ad70f0e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.MacAddress; import android.net.wifi.WifiConfiguration; +import android.os.Handler; import android.os.test.TestLooper; import androidx.test.filters.SmallTest; @@ -63,7 +64,7 @@ import java.util.Random; * Unit tests for {@link com.android.server.wifi.WifiConfigStore}. */ @SmallTest -public class WifiConfigStoreTest { +public class WifiConfigStoreTest extends WifiBaseTest { private static final String TEST_USER_DATA = "UserData"; private static final String TEST_SHARE_DATA = "ShareData"; private static final String TEST_CREATOR_NAME = "CreatorName"; @@ -87,6 +88,7 @@ public class WifiConfigStoreTest { + "<string name=\"SSID\">%s</string>\n" + "<null name=\"BSSID\" />\n" + "<null name=\"PreSharedKey\" />\n" + + "<null name=\"SaePasswordId\" />\n" + "<null name=\"WEPKeys\" />\n" + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n" + "<boolean name=\"HiddenSSID\" value=\"false\" />\n" @@ -218,8 +220,8 @@ public class WifiConfigStoreTest { public void setUp() throws Exception { setupMocks(); - mWifiConfigStore = new WifiConfigStore(mContext, mLooper.getLooper(), mClock, mWifiMetrics, - mSharedStore); + mWifiConfigStore = new WifiConfigStore(mContext, new Handler(mLooper.getLooper()), mClock, + mWifiMetrics, mSharedStore); // Enable verbose logging before tests. mWifiConfigStore.enableVerboseLogging(true); } @@ -745,13 +747,10 @@ public class WifiConfigStoreTest { } /** - * Verify that a XmlPullParserException will be thrown when reading an user store file - * containing unknown data. - * - * @throws Exception + * Verify that we gracefully skip unknown section when reading an user store file. */ - @Test(expected = XmlPullParserException.class) - public void testReadUserStoreContainedUnknownData() throws Exception { + @Test + public void testReadUserStoreContainedUnknownSection() throws Exception { String storeFileData = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<WifiConfigStoreData>\n" @@ -764,13 +763,10 @@ public class WifiConfigStoreTest { } /** - * Verify that a XmlPullParserException will be thrown when reading the share store file - * containing unknown data. - * - * @throws Exception + * Verify that we gracefully skip unknown section when reading a shared store file. */ - @Test(expected = XmlPullParserException.class) - public void testReadShareStoreContainedUnknownData() throws Exception { + @Test + public void testReadShareStoreContainedUnknownSection() throws Exception { String storeFileData = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<WifiConfigStoreData>\n" diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java index 056df7c370..9e2783e1b2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java @@ -508,8 +508,7 @@ public class WifiConfigurationTestUtil { /** * Gets scan result capabilities for a WPA2/WPA3-Transition mode network configuration */ - private static String - getScanResultCapsForWpa2Wpa3TransitionNetwork(WifiConfiguration configuration) { + public static String getScanResultCapsForWpa2Wpa3TransitionNetwork() { String caps = "[RSN-PSK+SAE-CCMP]"; return caps; } @@ -532,7 +531,7 @@ public class WifiConfigurationTestUtil { public static ScanDetail createScanDetailForWpa2Wpa3TransitionModeNetwork( WifiConfiguration configuration, String bssid, int level, int frequency, long tsf, long seen) { - String caps = getScanResultCapsForWpa2Wpa3TransitionNetwork(configuration); + String caps = getScanResultCapsForWpa2Wpa3TransitionNetwork(); WifiSsid ssid = WifiSsid.createFromAsciiEncoded(configuration.getPrintableSsid()); return new ScanDetail(ssid, bssid, caps, level, frequency, tsf, seen); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java index c1640ce919..a241a1c42f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java @@ -25,8 +25,8 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiNetworkSpecifier; import android.net.wifi.WifiScanner; +import android.os.Binder; import android.os.PatternMatcher; -import android.os.UserHandle; import android.util.Pair; import androidx.test.filters.SmallTest; @@ -39,11 +39,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import javax.crypto.Mac; + /** * Unit tests for {@link com.android.server.wifi.WifiConfigurationUtil}. */ @SmallTest -public class WifiConfigurationUtilTest { +public class WifiConfigurationUtilTest extends WifiBaseTest { static final int CURRENT_USER_ID = 0; static final int CURRENT_USER_MANAGED_PROFILE_USER_ID = 10; static final int OTHER_USER_ID = 11; @@ -58,29 +60,6 @@ public class WifiConfigurationUtilTest { new UserInfo(CURRENT_USER_MANAGED_PROFILE_USER_ID, "managed profile", 0)); /** - * Test for {@link WifiConfigurationUtil.isVisibleToAnyProfile}. - */ - @Test - public void isVisibleToAnyProfile() { - // Shared network configuration created by another user. - final WifiConfiguration configuration = new WifiConfiguration(); - configuration.creatorUid = UserHandle.getUid(OTHER_USER_ID, 0); - assertTrue(WifiConfigurationUtil.isVisibleToAnyProfile(configuration, PROFILES)); - - // Private network configuration created by another user. - configuration.shared = false; - assertFalse(WifiConfigurationUtil.isVisibleToAnyProfile(configuration, PROFILES)); - - // Private network configuration created by the current user. - configuration.creatorUid = UserHandle.getUid(CURRENT_USER_ID, 0); - assertTrue(WifiConfigurationUtil.isVisibleToAnyProfile(configuration, PROFILES)); - - // Private network configuration created by the current user's managed profile. - configuration.creatorUid = UserHandle.getUid(CURRENT_USER_MANAGED_PROFILE_USER_ID, 0); - assertTrue(WifiConfigurationUtil.isVisibleToAnyProfile(configuration, PROFILES)); - } - - /** * Verify that new WifiEnterpriseConfig is detected. */ @Test @@ -961,6 +940,32 @@ public class WifiConfigurationUtilTest { existingConfig, newConfig)); } + /** + * Verifies that calculatePersistentMacForConfiguration produces persistent, locally generated + * MAC addresses that are valid for MAC randomization. + */ + @Test + public void testCalculatePersistentMacForConfiguration() { + // verify null inputs + assertNull(WifiConfigurationUtil.calculatePersistentMacForConfiguration(null, null)); + + // Verify that a the MAC address calculated is valid + int uid = Binder.getCallingUid(); + for (int i = 0; i < 10; i++) { + WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + Mac hashFunction = WifiConfigurationUtil.obtainMacRandHashFunction(uid); + MacAddress macAddress = WifiConfigurationUtil.calculatePersistentMacForConfiguration( + config, hashFunction); + assertTrue(WifiConfiguration.isValidMacAddressForRandomization(macAddress)); + + // Verify that the secret used to generate MAC address is persistent + Mac hashFunction2 = WifiConfigurationUtil.obtainMacRandHashFunction(uid); + MacAddress macAddress2 = WifiConfigurationUtil.calculatePersistentMacForConfiguration( + config, hashFunction2); + assertEquals(macAddress, macAddress2); + } + } + private static class EnterpriseConfig { public String eap; public String phase2; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java index 786e257ac0..bd74f51bc6 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java @@ -39,7 +39,7 @@ import java.util.ArrayList; * Unit tests for {@link com.android.server.wifi.WifiConnectivityHelper}. */ @SmallTest -public class WifiConnectivityHelperTest { +public class WifiConnectivityHelperTest extends WifiBaseTest { /** Sets up test. */ @Before public void setUp() throws Exception { diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java index c1686b48d6..d32a312051 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java @@ -16,9 +16,6 @@ package com.android.server.wifi; -import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE; -import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; - import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; @@ -45,6 +42,7 @@ import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiScanner.ScanListener; import android.net.wifi.WifiScanner.ScanSettings; import android.net.wifi.WifiSsid; +import android.os.Handler; import android.os.Process; import android.os.SystemClock; import android.os.WorkSource; @@ -77,7 +75,7 @@ import java.util.stream.Collectors; * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}. */ @SmallTest -public class WifiConnectivityManagerTest { +public class WifiConnectivityManagerTest extends WifiBaseTest { /** * Called before each test */ @@ -101,7 +99,7 @@ public class WifiConnectivityManagerTest { when(mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList()) .thenReturn(new ArrayList<>()); mWifiConnectivityManager = createConnectivityManager(); - verify(mWifiConfigManager).setOnSavedNetworkUpdateListener(anyObject()); + verify(mWifiConfigManager).addOnNetworkUpdateListener(anyObject()); mWifiConnectivityManager.setTrustedConnectionAllowed(true); mWifiConnectivityManager.setWifiEnabled(true); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); @@ -140,7 +138,6 @@ public class WifiConnectivityManagerTest { @Mock private Clock mClock; @Mock private WifiLastResortWatchdog mWifiLastResortWatchdog; @Mock private OpenNetworkNotifier mOpenNetworkNotifier; - @Mock private CarrierNetworkNotifier mCarrierNetworkNotifier; @Mock private CarrierNetworkConfig mCarrierNetworkConfig; @Mock private WifiMetrics mWifiMetrics; @Mock private WifiNetworkScoreCache mScoreCache; @@ -148,8 +145,8 @@ public class WifiConnectivityManagerTest { @Captor ArgumentCaptor<ScanResult> mCandidateScanResultCaptor; @Captor ArgumentCaptor<ArrayList<String>> mBssidBlacklistCaptor; @Captor ArgumentCaptor<ArrayList<String>> mSsidWhitelistCaptor; - @Captor ArgumentCaptor<WifiConfigManager.OnSavedNetworkUpdateListener> - mSavedNetworkUpdateListenerCaptor; + @Captor ArgumentCaptor<WifiConfigManager.OnNetworkUpdateListener> + mNetworkUpdateListenerCaptor; private MockResources mResources; private int mFullScanMaxTxPacketRate; private int mFullScanMaxRxPacketRate; @@ -315,8 +312,8 @@ public class WifiConnectivityManagerTest { pnoNetworkList.add(pnoNetwork); when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList); when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList); - doNothing().when(wifiConfigManager).setOnSavedNetworkUpdateListener( - mSavedNetworkUpdateListenerCaptor.capture()); + doNothing().when(wifiConfigManager).addOnNetworkUpdateListener( + mNetworkUpdateListenerCaptor.capture()); return wifiConfigManager; } @@ -326,8 +323,9 @@ public class WifiConnectivityManagerTest { new ScoringParams(mContext), mClientModeImpl, mWifiInjector, mWifiConfigManager, mWifiInfo, mWifiNS, mWifiConnectivityHelper, - mWifiLastResortWatchdog, mOpenNetworkNotifier, mCarrierNetworkNotifier, - mCarrierNetworkConfig, mWifiMetrics, mLooper.getLooper(), mClock, mLocalLog); + mWifiLastResortWatchdog, mOpenNetworkNotifier, + mCarrierNetworkConfig, mWifiMetrics, new Handler(mLooper.getLooper()), mClock, + mLocalLog); } /** @@ -757,150 +755,6 @@ public class WifiConnectivityManagerTest { } /** - * {@link CarrierNetworkNotifier} handles scan results on network selection. - * - * Expected behavior: CarrierNetworkNotifier handles scan results - */ - @Test - public void wifiDisconnected_noConnectionCandidate_CarrierNetworkNotifierScanResultsHandled() { - // no connection candidate selected - when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(), - anyBoolean(), anyBoolean())).thenReturn(null); - - List<ScanDetail> expectedCarrierNetworks = new ArrayList<>(); - expectedCarrierNetworks.add( - new ScanDetail( - new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID), - CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "[EAP][ESS]", -78, 2450, - 1025, 22, 33, 20, 0, 0, true), null)); - - when(mWifiNS.getFilteredScanDetailsForCarrierUnsavedNetworks(any())) - .thenReturn(expectedCarrierNetworks); - - // Set WiFi to disconnected state to trigger PNO scan - mWifiConnectivityManager.handleConnectionStateChanged( - WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - - verify(mCarrierNetworkNotifier).handleScanResults(expectedCarrierNetworks); - } - - /** - * {@link CarrierNetworkNotifier} does not handle scan results on network selection if carrier - * encryption info is not available. - * - * Expected behavior: CarrierNetworkNotifier does not handle scan results - */ - @Test - public void whenNoEncryptionInfoAvailable_CarrierNetworkNotifierDoesNotHandleScanResults() { - // no connection candidate selected - when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(), - anyBoolean(), anyBoolean())).thenReturn(null); - - List<ScanDetail> expectedCarrierNetworks = new ArrayList<>(); - expectedCarrierNetworks.add( - new ScanDetail( - new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID), - CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "[EAP][ESS]", -78, 2450, - 1025, 22, 33, 20, 0, 0, true), null)); - - when(mWifiNS.getFilteredScanDetailsForCarrierUnsavedNetworks(any())) - .thenReturn(expectedCarrierNetworks); - when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(false); - - // Set WiFi to disconnected state to trigger PNO scan - mWifiConnectivityManager.handleConnectionStateChanged( - WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - - verify(mCarrierNetworkNotifier, never()).handleScanResults(expectedCarrierNetworks); - } - - /** - * When wifi is connected, {@link CarrierNetworkNotifier} handles the Wi-Fi connected behavior. - * - * Expected behavior: CarrierNetworkNotifier handles connected behavior - */ - @Test - public void wifiConnected_carrierNetworkNotifierHandlesConnection() { - // Set WiFi to connected state - mWifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID)); - mWifiConnectivityManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE); - verify(mCarrierNetworkNotifier).handleWifiConnected(CANDIDATE_SSID); - } - - /** - * When wifi is connected, {@link CarrierNetworkNotifier} handles connection state - * change. - * - * Expected behavior: CarrierNetworkNotifer does not clear pending notification. - */ - @Test - public void wifiDisconnected_carrierNetworkNotifierDoesNotClearPendingNotification() { - // Set WiFi to disconnected state - mWifiConnectivityManager.handleConnectionStateChanged( - WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - - verify(mCarrierNetworkNotifier, never()).clearPendingNotification(anyBoolean()); - } - - /** - * When a Wi-Fi connection attempt ends, {@link CarrierNetworkNotifier} handles the connection - * failure. A failure code that is not {@link WifiMetrics.ConnectionEvent#FAILURE_NONE} - * represents a connection failure. - * - * Expected behavior: CarrierNetworkNotifier handles connection failure. - */ - @Test - public void wifiConnectionEndsWithFailure_carrierNetworkNotifierHandlesConnectionFailure() { - mWifiConnectivityManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED); - - verify(mCarrierNetworkNotifier).handleConnectionFailure(); - } - - /** - * When a Wi-Fi connection attempt ends, {@link CarrierNetworkNotifier} does not handle - * connection failure after a successful connection. - * {@link WifiMetrics.ConnectionEvent#FAILURE_NONE} represents a successful connection. - * - * Expected behavior: CarrierNetworkNotifier does nothing. - */ - @Test - public void - wifiConnectionEndsWithSuccess_carrierNetworkNotifierDoesNotHandleConnectionFailure() { - mWifiConnectivityManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE); - - verify(mCarrierNetworkNotifier, never()).handleConnectionFailure(); - } - - /** - * When Wi-Fi is disabled, clear the pending notification and reset notification repeat delay. - * - * Expected behavior: clear pending notification and reset notification repeat delay - * */ - @Test - public void carrierNetworkNotifierClearsPendingNotificationOnWifiDisabled() { - mWifiConnectivityManager.setWifiEnabled(false); - - verify(mCarrierNetworkNotifier).clearPendingNotification(true /* resetRepeatDelay */); - } - - /** - * Verify that the CarrierNetworkNotifier tracks screen state changes. - */ - @Test - public void carrierNetworkNotifierTracksScreenStateChanges() { - mWifiConnectivityManager.handleScreenStateChanged(false); - - verify(mCarrierNetworkNotifier).handleScreenStateChanged(false); - - mWifiConnectivityManager.handleScreenStateChanged(true); - - verify(mCarrierNetworkNotifier).handleScreenStateChanged(true); - } - - /** * Verify that scan interval for screen on and wifi disconnected scenario * is in the exponential backoff fashion. * @@ -2115,23 +1969,6 @@ public class WifiConnectivityManagerTest { } /** - * Disabling the network temporarily due to lack of internet is a special reason for which we - * don't want WCM to trigger a disconnect (by removing the network from supplicant). - */ - @Test - public void dontDisconnectIfNetworkTemporarilyDisabledDueToNoInternet() { - assertNotNull(mSavedNetworkUpdateListenerCaptor.getValue()); - - mSavedNetworkUpdateListenerCaptor.getValue() - .onSavedNetworkPermanentlyDisabled(0, DISABLED_AUTHENTICATION_FAILURE); - verify(mWifiConnectivityHelper).removeNetworkIfCurrent(0); - - mSavedNetworkUpdateListenerCaptor.getValue() - .onSavedNetworkPermanentlyDisabled(0, DISABLED_NO_INTERNET_TEMPORARY); - // Don't remove network. - } - - /** * Verify the various WifiConnectivityManager enable/disable sequences. * * Expected behavior: WifiConnectivityManager is turned on as a long as there is diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java deleted file mode 100644 index 18f7ea5df3..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java +++ /dev/null @@ -1,1126 +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 com.android.server.wifi.WifiController.CMD_AP_STOPPED; -import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_RECOVERY_DISABLE_WIFI; -import static com.android.server.wifi.WifiController.CMD_RECOVERY_RESTART_WIFI; -import static com.android.server.wifi.WifiController.CMD_SCANNING_STOPPED; -import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_SET_AP; -import static com.android.server.wifi.WifiController.CMD_STA_STOPPED; -import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.location.LocationManager; -import android.net.wifi.WifiManager; -import android.os.test.TestLooper; -import android.util.Log; - -import androidx.test.filters.SmallTest; - -import com.android.internal.R; -import com.android.internal.util.IState; -import com.android.internal.util.StateMachine; -import com.android.server.wifi.util.WifiPermissionsUtil; - -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.ByteArrayOutputStream; -import java.io.PrintWriter; -import java.lang.reflect.Method; - -/** - * Test WifiController for changes in and out of ECM and SoftAP modes. - */ -@SmallTest -public class WifiControllerTest { - - private static final String TAG = "WifiControllerTest"; - - private static final int TEST_WIFI_RECOVERY_DELAY_MS = 2000; - - private void dumpState() { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - PrintWriter writer = new PrintWriter(stream); - mWifiController.dump(null, writer, null); - writer.flush(); - Log.d(TAG, "ClientModeImpl state -" + stream.toString()); - } - - private IState getCurrentState() throws Exception { - Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); - method.setAccessible(true); - return (IState) method.invoke(mWifiController); - } - - private void initializeSettingsStore() throws Exception { - when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); - when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); - } - - TestLooper mLooper; - @Mock Context mContext; - @Mock Resources mResources; - @Mock FrameworkFacade mFacade; - @Mock WifiSettingsStore mSettingsStore; - @Mock ClientModeImpl mClientModeImpl; - @Mock ActiveModeWarden mActiveModeWarden; - @Mock WifiPermissionsUtil mWifiPermissionsUtil; - - WifiController mWifiController; - - private BroadcastReceiver mBroadcastReceiver; - - private ClientModeManager.Listener mClientModeCallback; - private ScanOnlyModeManager.Listener mScanOnlyModeCallback; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mLooper = new TestLooper(); - - initializeSettingsStore(); - - when(mContext.getResources()).thenReturn(mResources); - - when(mResources.getInteger(R.integer.config_wifi_framework_recovery_timeout_delay)) - .thenReturn(TEST_WIFI_RECOVERY_DELAY_MS); - when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - - mWifiController = new WifiController(mContext, mClientModeImpl, mLooper.getLooper(), - mSettingsStore, mLooper.getLooper(), mFacade, mActiveModeWarden, - mWifiPermissionsUtil); - mWifiController.start(); - mLooper.dispatchAll(); - - ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass( - BroadcastReceiver.class); - verify(mContext).registerReceiver(bcastRxCaptor.capture(), any(IntentFilter.class)); - mBroadcastReceiver = bcastRxCaptor.getValue(); - - ArgumentCaptor<ClientModeManager.Listener> clientModeCallbackCaptor = - ArgumentCaptor.forClass(ClientModeManager.Listener.class); - verify(mActiveModeWarden).registerClientModeCallback(clientModeCallbackCaptor.capture()); - mClientModeCallback = clientModeCallbackCaptor.getValue(); - - ArgumentCaptor<ScanOnlyModeManager.Listener> scanOnlyModeCallbackCaptor = - ArgumentCaptor.forClass(ScanOnlyModeManager.Listener.class); - verify(mActiveModeWarden).registerScanOnlyCallback(scanOnlyModeCallbackCaptor.capture()); - mScanOnlyModeCallback = scanOnlyModeCallbackCaptor.getValue(); - - } - - @After - public void cleanUp() { - mLooper.dispatchAll(); - } - - /** - * Verify that toggling wifi from disabled starts client mode. - */ - @Test - public void enableWifi() throws Exception { - assertEquals("StaDisabledState", getCurrentState().getName()); - - when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); - mWifiController.sendMessage(CMD_WIFI_TOGGLED); - mLooper.dispatchAll(); - assertEquals("StaEnabledState", getCurrentState().getName()); - } - - /** - * Test verifying that we can enter scan mode when the scan mode changes - */ - @Test - public void enableScanMode() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterScanOnlyMode(); - } - - /** - * Verify that if scanning is enabled at startup, we enter scan mode - */ - @Test - public void testEnterScanModeAtStartWhenSet() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - - // reset to avoid the default behavior - reset(mActiveModeWarden); - - WifiController wifiController = - new WifiController(mContext, mClientModeImpl, mLooper.getLooper(), - mSettingsStore, mLooper.getLooper(), mFacade, - mActiveModeWarden, mWifiPermissionsUtil); - - wifiController.start(); - mLooper.dispatchAll(); - - verify(mActiveModeWarden, never()).disableWifi(); - verify(mActiveModeWarden).enterScanOnlyMode(); - } - - /** - * Do not enter scan mode if location mode disabled. - */ - @Test - public void testDoesNotEnterScanModeWhenLocationModeDisabled() throws Exception { - // Start a new WifiController with wifi disabled - when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); - when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); - when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); - - mWifiController = new WifiController(mContext, mClientModeImpl, mLooper.getLooper(), - mSettingsStore, mLooper.getLooper(), mFacade, mActiveModeWarden, - mWifiPermissionsUtil); - - reset(mActiveModeWarden); - mWifiController.start(); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).disableWifi(); - - // toggling scan always available is not sufficient for scan mode - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - mLooper.dispatchAll(); - - verify(mActiveModeWarden, never()).enterScanOnlyMode(); - - } - - /** - * Only enter scan mode if location mode enabled - */ - @Test - public void testEnterScanModeWhenLocationModeEnabled() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); - - reset(mContext, mActiveModeWarden); - when(mContext.getResources()).thenReturn(mResources); - mWifiController = new WifiController(mContext, mClientModeImpl, mLooper.getLooper(), - mSettingsStore, mLooper.getLooper(), mFacade, mActiveModeWarden, - mWifiPermissionsUtil); - - mWifiController.start(); - mLooper.dispatchAll(); - - ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass( - BroadcastReceiver.class); - verify(mContext).registerReceiver(bcastRxCaptor.capture(), any(IntentFilter.class)); - mBroadcastReceiver = bcastRxCaptor.getValue(); - - verify(mActiveModeWarden).disableWifi(); - - when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION); - - mBroadcastReceiver.onReceive(mContext, intent); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterScanOnlyMode(); - } - - - - /** - * Disabling location mode when in scan mode will disable wifi - */ - @Test - public void testExitScanModeWhenLocationModeDisabled() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - - reset(mContext, mActiveModeWarden); - when(mContext.getResources()).thenReturn(mResources); - mWifiController = new WifiController(mContext, mClientModeImpl, mLooper.getLooper(), - mSettingsStore, mLooper.getLooper(), mFacade, mActiveModeWarden, - mWifiPermissionsUtil); - mWifiController.start(); - mLooper.dispatchAll(); - - ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass( - BroadcastReceiver.class); - verify(mContext).registerReceiver(bcastRxCaptor.capture(), any(IntentFilter.class)); - mBroadcastReceiver = bcastRxCaptor.getValue(); - - verify(mActiveModeWarden).enterScanOnlyMode(); - - when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); - Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION); - - mBroadcastReceiver.onReceive(mContext, intent); - mLooper.dispatchAll(); - verify(mActiveModeWarden).disableWifi(); - } - - /** - * When in Client mode, make sure ECM triggers wifi shutdown. - */ - @Test - public void testEcmOnFromClientMode() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); - enableWifi(); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).shutdownWifi(); - } - - /** - * ECM disabling messages, when in client mode (not expected) do not trigger state changes. - */ - @Test - public void testEcmOffInClientMode() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); - enableWifi(); - - // Test with WifiDisableInECBM turned off - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden, never()).shutdownWifi(); - verify(mActiveModeWarden).stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); - } - - /** - * When ECM activates and we are in client mode, disabling ECM should return us to client mode. - */ - @Test - public void testEcmDisabledReturnsToClientMode() throws Exception { - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).shutdownWifi(); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).enterClientMode(); - } - - /** - * When Ecm mode is enabled, we should shut down wifi when we get an emergency mode changed - * update. - */ - @Test - public void testEcmOnFromScanMode() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterScanOnlyMode(); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - verify(mActiveModeWarden).enterScanOnlyMode(); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).shutdownWifi(); - } - - /** - * When Ecm mode is disabled, we should not shut down scan mode if we get an emergency mode - * changed update, but we should turn off soft AP - */ - @Test - public void testEcmOffInScanMode() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterScanOnlyMode(); - - // Test with WifiDisableInECBM turned off: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false); - verify(mActiveModeWarden).enterScanOnlyMode(); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden, never()).shutdownWifi(); - verify(mActiveModeWarden).stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); - } - - /** - * When ECM is disabled, we should return to scan mode - */ - @Test - public void testEcmDisabledReturnsToScanMode() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterScanOnlyMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).shutdownWifi(); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).enterScanOnlyMode(); - } - - /** - * When Ecm mode is enabled, we should shut down wifi when we get an emergency mode changed - * update. - */ - @Test - public void testEcmOnFromSoftApMode() throws Exception { - mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); - mLooper.dispatchAll(); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - verify(mActiveModeWarden).enterSoftAPMode(any()); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).shutdownWifi(); - } - - /** - * When Ecm mode is disabled, we should shut down softap mode if we get an emergency mode - * changed update - */ - @Test - public void testEcmOffInSoftApMode() throws Exception { - mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterSoftAPMode(any()); - - // Test with WifiDisableInECBM turned off: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); - } - - /** - * When ECM is activated and we were in softap mode, we should just return to wifi off when ECM - * ends - */ - @Test - public void testEcmDisabledRemainsDisabledWhenSoftApHadBeenOn() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterSoftAPMode(any()); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - reset(mActiveModeWarden); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - - verify(mActiveModeWarden).disableWifi(); - // no additional calls to enable softap - verify(mActiveModeWarden, never()).enterSoftAPMode(any()); - } - - /** - * Wifi should remain off when already disabled and we enter ECM. - */ - @Test - public void testEcmOnFromDisabledMode() throws Exception { - verify(mActiveModeWarden, never()).enterSoftAPMode(any()); - verify(mActiveModeWarden, never()).enterClientMode(); - verify(mActiveModeWarden, never()).enterScanOnlyMode(); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verify(mActiveModeWarden).shutdownWifi(); - } - - - /** - * Updates about call state change also trigger entry of ECM mode. - */ - @Test - public void testEnterEcmOnEmergencyCallStateChange() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test call state changed - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterClientMode(); - } - - /** - * Updates about call state change with an invalid state do not change modes. - */ - @Test - public void testEnterEcmOnEmergencyCallStateChangeAndUpdateWithInvalidState() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test call state changed - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 2); - mLooper.dispatchAll(); - verifyNoMoreInteractions(mActiveModeWarden); - } - - /** - * Updates about emergency mode change with an invalid state do not change modes. - */ - @Test - public void testEnterEcmOnEmergencyModeChangeAndUpdateWithInvalidState() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test call state changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 2); - mLooper.dispatchAll(); - verifyNoMoreInteractions(mActiveModeWarden); - } - - /** - * Verify when both ECM and call state changes arrive, we enter ECM mode - */ - @Test - public void testEnterEcmWithBothSignals() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - // still only 1 shutdown - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); - mLooper.dispatchAll(); - // stay in ecm, do not send an additional client mode trigger - verify(mActiveModeWarden, never()).enterClientMode(); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - // now we can re-enable wifi - verify(mActiveModeWarden).enterClientMode(); - } - - /** - * Verify when both ECM and call state changes arrive but out of order, we enter ECM mode - */ - @Test - public void testEnterEcmWithBothSignalsOutOfOrder() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mLooper.dispatchAll(); - // still only 1 shutdown - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); - mLooper.dispatchAll(); - // stay in ecm, do not send an additional client mode trigger - verify(mActiveModeWarden, never()).enterClientMode(); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - // now we can re-enable wifi - verify(mActiveModeWarden).enterClientMode(); - } - - /** - * Verify when both ECM and call state changes arrive but completely out of order, - * we still enter and properly exit ECM mode - */ - @Test - public void testEnterEcmWithBothSignalsOppositeOrder() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - // still only 1 shutdown - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - // stay in ecm, do not send an additional client mode trigger - verify(mActiveModeWarden, never()).enterClientMode(); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); - mLooper.dispatchAll(); - // now we can re-enable wifi - verify(mActiveModeWarden).enterClientMode(); - } - - - /** - * When ECM is active, we might get addition signals of ECM mode, we must not exit until they - * are all cleared. - */ - @Test - public void testProperExitFromEcmModeWithMultipleMessages() throws Exception { - verify(mActiveModeWarden).disableWifi(); - - enableWifi(); - verify(mActiveModeWarden).enterClientMode(); - - reset(mActiveModeWarden); - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); - mLooper.dispatchAll(); - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); - mLooper.dispatchAll(); - verify(mActiveModeWarden, never()).enterClientMode(); - - // now we will exit ECM - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); - mLooper.dispatchAll(); - - // now we can re-enable wifi - verify(mActiveModeWarden).enterClientMode(); - } - - /** - * Toggling wifi when in ECM does not exit ecm mode and enable wifi - */ - @Test - public void testWifiDoesNotToggleOnWhenInEcm() throws Exception { - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verify(mActiveModeWarden).shutdownWifi(); - - // now toggle wifi and verify we do not start wifi - when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); - mWifiController.sendMessage(CMD_WIFI_TOGGLED); - mLooper.dispatchAll(); - - verify(mActiveModeWarden, never()).enterClientMode(); - } - - /** - * Toggling scan mode when in ECM does not exit ecm mode and enable scan mode - */ - @Test - public void testScanModeDoesNotToggleOnWhenInEcm() throws Exception { - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verify(mActiveModeWarden).shutdownWifi(); - - // now enable scanning and verify we do not start wifi - when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - mLooper.dispatchAll(); - - verify(mActiveModeWarden, never()).enterScanOnlyMode(); - } - - /** - * Toggling softap mode when in ECM does not exit ecm mode and enable softap - */ - @Test - public void testSoftApModeDoesNotToggleOnWhenInEcm() throws Exception { - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verify(mActiveModeWarden).shutdownWifi(); - - mWifiController.sendMessage(CMD_SET_AP); - mLooper.dispatchAll(); - - verify(mActiveModeWarden, never()).enterSoftAPMode(any()); - } - - /** - * Toggling off softap mode when in ECM does not induce a mode change - */ - @Test - public void testSoftApStoppedDoesNotSwitchModesWhenInEcm() throws Exception { - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verify(mActiveModeWarden).shutdownWifi(); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_AP_STOPPED); - mLooper.dispatchAll(); - - verifyNoMoreInteractions(mActiveModeWarden); - } - - /** - * Toggling softap mode when in airplane mode needs to enable softap - */ - @Test - public void testSoftApModeToggleWhenInAirplaneMode() throws Exception { - // Test with airplane mode turned on: - when(mSettingsStore.isAirplaneModeOn()).thenReturn(true); - - // Turn on SoftAp. - mWifiController.sendMessage(CMD_SET_AP, 1); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterSoftAPMode(any()); - - // Turn off SoftAp. - mWifiController.sendMessage(CMD_SET_AP, 0, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - mLooper.dispatchAll(); - verify(mActiveModeWarden).stopSoftAPMode(WifiManager.IFACE_IP_MODE_UNSPECIFIED); - } - - /** - * Toggling off scan mode when in ECM does not induce a mode change - */ - @Test - public void testScanModeStoppedDoesNotSwitchModesWhenInEcm() throws Exception { - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verify(mActiveModeWarden).shutdownWifi(); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_SCANNING_STOPPED); - mLooper.dispatchAll(); - - verifyNoMoreInteractions(mActiveModeWarden); - } - - /** - * Toggling off client mode when in ECM does not induce a mode change - */ - @Test - public void testClientModeStoppedDoesNotSwitchModesWhenInEcm() throws Exception { - - // Test with WifiDisableInECBM turned on: - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - // test ecm changed - mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verify(mActiveModeWarden).shutdownWifi(); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_STA_STOPPED); - mLooper.dispatchAll(); - - verifyNoMoreInteractions(mActiveModeWarden); - } - - - /** - * When AP mode is enabled and wifi was previously in AP mode, we should return to - * StaEnabledState after the AP is disabled. - * Enter StaEnabledState, activate AP mode, disable AP mode. - * <p> - * Expected: AP should successfully start and exit, then return to StaEnabledState. - */ - @Test - public void testReturnToStaEnabledStateAfterAPModeShutdown() throws Exception { - enableWifi(); - assertEquals("StaEnabledState", getCurrentState().getName()); - - mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); - // add an "unexpected" sta mode stop to simulate a single interface device - mClientModeCallback.onStateChanged(WifiManager.WIFI_STATE_DISABLED); - mLooper.dispatchAll(); - - when(mSettingsStore.getWifiSavedState()).thenReturn(1); - mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget(); - mLooper.dispatchAll(); - - InOrder inOrder = inOrder(mActiveModeWarden); - inOrder.verify(mActiveModeWarden).enterClientMode(); - assertEquals("StaEnabledState", getCurrentState().getName()); - } - - /** - * When AP mode is enabled and wifi is toggled on, we should transition to - * StaEnabledState after the AP is disabled. - * Enter StaEnabledState, activate AP mode, toggle WiFi. - * <p> - * Expected: AP should successfully start and exit, then return to StaEnabledState. - */ - @Test - public void testReturnToStaEnabledStateAfterWifiEnabledShutdown() throws Exception { - enableWifi(); - assertEquals("StaEnabledState", getCurrentState().getName()); - - mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); - mLooper.dispatchAll(); - - when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); - mWifiController.obtainMessage(CMD_WIFI_TOGGLED).sendToTarget(); - mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget(); - mLooper.dispatchAll(); - - InOrder inOrder = inOrder(mActiveModeWarden); - inOrder.verify(mActiveModeWarden).enterClientMode(); - assertEquals("StaEnabledState", getCurrentState().getName()); - } - - @Test - public void testRestartWifiStackInStaEnabledStateTriggersBugReport() throws Exception { - enableWifi(); - mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI, - SelfRecovery.REASON_WIFINATIVE_FAILURE); - mLooper.dispatchAll(); - verify(mClientModeImpl).takeBugReport(anyString(), anyString()); - } - - @Test - public void testRestartWifiWatchdogDoesNotTriggerBugReport() throws Exception { - enableWifi(); - mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI, - SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - mLooper.dispatchAll(); - verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString()); - } - - /** - * When in sta mode, CMD_RECOVERY_DISABLE_WIFI messages should trigger wifi to disable. - */ - @Test - public void testRecoveryDisabledTurnsWifiOff() throws Exception { - enableWifi(); - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_RECOVERY_DISABLE_WIFI); - mLooper.dispatchAll(); - verify(mActiveModeWarden).disableWifi(); - } - - /** - * When wifi is disabled, CMD_RECOVERY_DISABLE_WIFI should not trigger a state change. - */ - @Test - public void testRecoveryDisabledWhenWifiAlreadyOff() throws Exception { - assertEquals("StaDisabledState", getCurrentState().getName()); - mWifiController.sendMessage(CMD_RECOVERY_DISABLE_WIFI); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - } - - /** - * The command to trigger a WiFi reset should not trigger any action by WifiController if we - * are not in STA mode. - * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures - * should be ignored. - * Create and start WifiController in StaDisabledState, send command to restart WiFi - * <p> - * Expected: WiFiController should not call ActiveModeWarden.disableWifi() - */ - @Test - public void testRestartWifiStackInStaDisabledState() throws Exception { - // Start a new WifiController with wifi disabled - when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); - when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); - - mWifiController = new WifiController(mContext, mClientModeImpl, mLooper.getLooper(), - mSettingsStore, mLooper.getLooper(), mFacade, mActiveModeWarden, - mWifiPermissionsUtil); - - mWifiController.start(); - mLooper.dispatchAll(); - - reset(mClientModeImpl); - assertEquals("StaDisabledState", getCurrentState().getName()); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); - mLooper.dispatchAll(); - verify(mActiveModeWarden).disableWifi(); - } - - /** - * The command to trigger a WiFi reset should not trigger any action by WifiController if we - * are not in STA mode, even if scans are allowed. - * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures - * should be ignored. - * Create and start WifiController in StaDisabledState, send command to restart WiFi - * <p> - * Expected: WiFiController should not call ActiveModeWarden.disableWifi() or - * ActiveModeWarden.shutdownWifi(). - */ - @Test - public void testRestartWifiStackInStaDisabledWithScanState() throws Exception { - when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); - mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterScanOnlyMode(); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); - mLooper.dispatchAll(); - mLooper.moveTimeForward(TEST_WIFI_RECOVERY_DELAY_MS); - mLooper.dispatchAll(); - InOrder inOrder = inOrder(mActiveModeWarden); - verify(mActiveModeWarden).disableWifi(); - verify(mActiveModeWarden).enterScanOnlyMode(); - } - - /** - * The command to trigger a WiFi reset should trigger a wifi reset in ClientModeImpl through - * the ActiveModeWarden.shutdownWifi() call when in STA mode. - * WiFi is in connect mode, calls to reset the wifi stack due to connection failures - * should trigger a supplicant stop, and subsequently, a driver reload. - * Create and start WifiController in StaEnabledState, send command to restart WiFi - * <p> - * Expected: WiFiController should call ActiveModeWarden.shutdownWifi() and - * ActiveModeWarden should enter CONNECT_MODE and the wifi driver should be started. - */ - @Test - public void testRestartWifiStackInStaEnabledState() throws Exception { - enableWifi(); - assertEquals("StaEnabledState", getCurrentState().getName()); - - mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); - mLooper.dispatchAll(); - mLooper.moveTimeForward(TEST_WIFI_RECOVERY_DELAY_MS); - mLooper.dispatchAll(); - - InOrder inOrder = inOrder(mActiveModeWarden); - inOrder.verify(mActiveModeWarden).shutdownWifi(); - inOrder.verify(mActiveModeWarden).enterClientMode(); - assertEquals("StaEnabledState", getCurrentState().getName()); - } - - /** - * The command to trigger a WiFi reset should not trigger a reset when in ECM mode. - * Enable wifi and enter ECM state, send command to restart wifi. - * <p> - * Expected: The command to trigger a wifi reset should be ignored and we should remain in ECM - * mode. - */ - @Test - public void testRestartWifiStackDoesNotExitECMMode() throws Exception { - enableWifi(); - assertEquals("StaEnabledState", getCurrentState().getName()); - when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); - - mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - verify(mActiveModeWarden).shutdownWifi(); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); - mLooper.dispatchAll(); - assertEquals("EcmState", getCurrentState().getName()); - - verifyZeroInteractions(mActiveModeWarden); - } - - /** - * The command to trigger a WiFi reset should trigger a reset when in AP mode. - * Enter AP mode, send command to restart wifi. - * <p> - * Expected: The command to trigger a wifi reset should trigger wifi shutdown. - */ - @Test - public void testRestartWifiStackFullyStopsWifi() throws Exception { - mWifiController.obtainMessage(CMD_SET_AP, 1).sendToTarget(); - mLooper.dispatchAll(); - verify(mActiveModeWarden).enterSoftAPMode(any()); - - reset(mActiveModeWarden); - mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); - mLooper.dispatchAll(); - verify(mActiveModeWarden).shutdownWifi(); - } -} diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java index e0acd1f80c..3a2583bdbe 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java @@ -20,14 +20,22 @@ import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.telephony.TelephonyManager; + import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -39,13 +47,18 @@ import java.util.Locale; * Unit tests for {@link com.android.server.wifi.WifiCountryCode}. */ @SmallTest -public class WifiCountryCodeTest { +public class WifiCountryCodeTest extends WifiBaseTest { private static final String TAG = "WifiCountryCodeTest"; private String mDefaultCountryCode = "US"; private String mTelephonyCountryCode = "JP"; private boolean mRevertCountryCodeOnCellularLoss = true; + @Mock Context mContext; + @Mock TelephonyManager mTelephonyManager; + @Mock Handler mHandler; @Mock WifiNative mWifiNative; + private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); private WifiCountryCode mWifiCountryCode; /** @@ -56,11 +69,28 @@ public class WifiCountryCodeTest { MockitoAnnotations.initMocks(this); when(mWifiNative.setCountryCode(any(), anyString())).thenReturn(true); + when(mContext.getSystemService(Context.TELEPHONY_SERVICE)) + .thenReturn(mTelephonyManager); + + createWifiCountryCode(); + } + private void createWifiCountryCode() { mWifiCountryCode = new WifiCountryCode( + mContext, + mHandler, mWifiNative, mDefaultCountryCode, mRevertCountryCodeOnCellularLoss); + verify(mContext, atLeastOnce()).registerReceiver( + mBroadcastReceiverCaptor.capture(), any(), any(), any()); + } + + private void sendCountryCodeChangedBroadcast(String countryCode) { + Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED); + intent.putExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY, countryCode); + assertNotNull(mBroadcastReceiverCaptor.getValue()); + mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); } /** @@ -78,12 +108,27 @@ public class WifiCountryCodeTest { } /** + * Test that we read the country code from telephony at bootup. + * @throws Exception + */ + @Test + public void useTelephonyCountryCodeOnBootup() throws Exception { + when(mTelephonyManager.getNetworkCountryIso()).thenReturn(mTelephonyCountryCode); + // Supplicant started. + mWifiCountryCode.setReadyForChange(true); + // Wifi get L2 connected. + mWifiCountryCode.setReadyForChange(false); + verify(mWifiNative).setCountryCode(any(), anyString()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); + } + + /** * Test if we receive country code from Telephony before supplicant starts. * @throws Exception */ @Test - public void useTelephonyCountryCode() throws Exception { - mWifiCountryCode.setCountryCode(mTelephonyCountryCode); + public void useTelephonyCountryCodeOnChange() throws Exception { + sendCountryCodeChangedBroadcast(mTelephonyCountryCode); assertEquals(null, mWifiCountryCode.getCountryCodeSentToDriver()); // Supplicant started. mWifiCountryCode.setReadyForChange(true); @@ -98,12 +143,12 @@ public class WifiCountryCodeTest { * @throws Exception */ @Test - public void setTelephonyCountryCodeAfterSupplicantStarts() throws Exception { + public void telephonyCountryCodeChangeAfterSupplicantStarts() throws Exception { // Supplicant starts. mWifiCountryCode.setReadyForChange(true); assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); // Telephony country code arrives. - mWifiCountryCode.setCountryCode(mTelephonyCountryCode); + sendCountryCodeChangedBroadcast(mTelephonyCountryCode); // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); verify(mWifiNative, times(2)).setCountryCode(any(), anyString()); @@ -115,13 +160,13 @@ public class WifiCountryCodeTest { * @throws Exception */ @Test - public void setTelephonyCountryCodeAfterL2Connected() throws Exception { + public void telephonyCountryCodeChangeAfterL2Connected() throws Exception { // Supplicant starts. mWifiCountryCode.setReadyForChange(true); // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); // Telephony country code arrives. - mWifiCountryCode.setCountryCode(mTelephonyCountryCode); + sendCountryCodeChangedBroadcast(mTelephonyCountryCode); // Telephony coutry code won't be applied at this time. assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); mWifiCountryCode.setReadyForChange(true); @@ -140,10 +185,10 @@ public class WifiCountryCodeTest { @Test public void resetCountryCodeWhenOutOfService() throws Exception { assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode()); - mWifiCountryCode.setCountryCode(mTelephonyCountryCode); + sendCountryCodeChangedBroadcast(mTelephonyCountryCode); assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode()); // Out of service. - mWifiCountryCode.setCountryCode(""); + sendCountryCodeChangedBroadcast(""); assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode()); } @@ -158,16 +203,14 @@ public class WifiCountryCodeTest { public void doNotResetCountryCodeWhenOutOfService() throws Exception { // Refresh mWifiCountryCode with |config_wifi_revert_country_code_on_cellular_loss| // setting to false. - mWifiCountryCode = new WifiCountryCode( - mWifiNative, - mDefaultCountryCode, - false /* config_wifi_revert_country_code_on_cellular_loss */); + mRevertCountryCodeOnCellularLoss = false; + createWifiCountryCode(); assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode()); - mWifiCountryCode.setCountryCode(mTelephonyCountryCode); + sendCountryCodeChangedBroadcast(mTelephonyCountryCode); assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode()); // Out of service. - mWifiCountryCode.setCountryCode(""); + sendCountryCodeChangedBroadcast(""); assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode()); } @@ -182,10 +225,8 @@ public class WifiCountryCodeTest { String telephonyCountryCodeLower = "il"; String telephonyCountryCodeUpper = "IL"; - mWifiCountryCode = new WifiCountryCode( - mWifiNative, - oemCountryCodeLower, - mRevertCountryCodeOnCellularLoss); + mDefaultCountryCode = oemCountryCodeLower; + createWifiCountryCode(); // Set the default locale to "tr" (Non US). Locale.setDefault(new Locale("tr")); @@ -195,7 +236,7 @@ public class WifiCountryCodeTest { verify(mWifiNative).setCountryCode(any(), eq(oemCountryCodeUpper)); // Now trigger a country code change using the telephony country code. - mWifiCountryCode.setCountryCode(telephonyCountryCodeLower); + sendCountryCodeChangedBroadcast(telephonyCountryCodeLower); verify(mWifiNative).setCountryCode(any(), eq(telephonyCountryCodeUpper)); } /** @@ -207,7 +248,9 @@ public class WifiCountryCodeTest { PrintWriter pw = new PrintWriter(sw); mWifiCountryCode = new WifiCountryCode( - null, + mContext, + mHandler, + mWifiNative, null, false /* config_wifi_revert_country_code_on_cellular_loss */); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java index 71b658923a..898409f1ad 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java @@ -17,7 +17,6 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.never; @@ -26,8 +25,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.wifi.WifiInfo; -import android.os.Looper; -import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.Settings; import androidx.test.filters.SmallTest; @@ -36,7 +33,6 @@ import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -44,7 +40,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiDataStall}. */ @SmallTest -public class WifiDataStallTest { +public class WifiDataStallTest extends WifiBaseTest { @Mock Context mContext; @Mock FrameworkFacade mFacade; @@ -52,13 +48,10 @@ public class WifiDataStallTest { WifiDataStall mWifiDataStall; @Mock Clock mClock; @Mock DeviceConfigFacade mDeviceConfigFacade; - @Mock Looper mClientModeImplLooper; @Mock WifiInfo mWifiInfo; private final WifiLinkLayerStats mOldLlStats = new WifiLinkLayerStats(); private final WifiLinkLayerStats mNewLlStats = new WifiLinkLayerStats(); - final ArgumentCaptor<OnPropertiesChangedListener> mOnPropertiesChangedListenerCaptor = - ArgumentCaptor.forClass(OnPropertiesChangedListener.class); /** * Sets up for unit test @@ -76,10 +69,10 @@ public class WifiDataStallTest { .thenReturn(WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT); when(mDeviceConfigFacade.getDataStallDurationMs()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); - when(mDeviceConfigFacade.getDataStallTxTputThrMbps()).thenReturn( - DeviceConfigFacade.DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS); - when(mDeviceConfigFacade.getDataStallRxTputThrMbps()).thenReturn( - DeviceConfigFacade.DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS); + when(mDeviceConfigFacade.getDataStallTxTputThrKbps()).thenReturn( + DeviceConfigFacade.DEFAULT_DATA_STALL_TX_TPUT_THR_KBPS); + when(mDeviceConfigFacade.getDataStallRxTputThrKbps()).thenReturn( + DeviceConfigFacade.DEFAULT_DATA_STALL_RX_TPUT_THR_KBPS); when(mDeviceConfigFacade.getDataStallTxPerThr()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_TX_PER_THR); when(mDeviceConfigFacade.getDataStallCcaLevelThr()).thenReturn( @@ -90,7 +83,7 @@ public class WifiDataStallTest { when(mWifiInfo.getBSSID()).thenReturn("5G_WiFi"); mWifiDataStall = new WifiDataStall(mContext, mFacade, mWifiMetrics, mDeviceConfigFacade, - mClientModeImplLooper, mClock); + mClock); mOldLlStats.txmpdu_be = 1000; mOldLlStats.retries_be = 1000; @@ -104,8 +97,6 @@ public class WifiDataStallTest { mNewLlStats.rxmpdu_be = mOldLlStats.rxmpdu_be; mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + WifiDataStall.MAX_MS_DELTA_FOR_DATA_STALL - 1; - verify(mDeviceConfigFacade).addOnPropertiesChangedListener(any(), - mOnPropertiesChangedListenerCaptor.capture()); } /** @@ -138,6 +129,26 @@ public class WifiDataStallTest { } /** + * Verify there is no data stall if tx tput is above the threshold + */ + @Test + public void verifyNoDataStallTxFailureWhenTxTputIsHigh() throws Exception { + when(mWifiInfo.getLinkSpeed()).thenReturn(867); + when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); + mNewLlStats.retries_be = mOldLlStats.retries_be; + + assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, + mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); + verifyUpdateWifiIsUnusableLinkLayerStats(); + when(mClock.getElapsedSinceBootMillis()).thenReturn( + 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); + assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, + mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); + verify(mWifiMetrics, never()).logWifiIsUnusableEvent( + WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); + } + + /** * Verify there is no data stall from tx failures if tx failures are not consecutively bad */ @Test @@ -203,8 +214,6 @@ public class WifiDataStallTest { when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); when(mDeviceConfigFacade.getDataStallDurationMs()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS + 1); - mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null); - assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); @@ -225,8 +234,6 @@ public class WifiDataStallTest { when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); when(mDeviceConfigFacade.getDataStallTxPerThr()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_TX_PER_THR + 1); - mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null); - assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); @@ -256,6 +263,7 @@ public class WifiDataStallTest { */ @Test public void verifyNoDataStallBigTimeGap() throws Exception { + mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT; mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + WifiDataStall.MAX_MS_DELTA_FOR_DATA_STALL + 1; assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java index 9883737951..e4b1d70296 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java @@ -59,7 +59,7 @@ import java.util.regex.Pattern; * Unit tests for {@link WifiDiagnostics}. */ @SmallTest -public class WifiDiagnosticsTest { +public class WifiDiagnosticsTest extends WifiBaseTest { @Mock WifiNative mWifiNative; @Mock BuildProperties mBuildProperties; @Mock Context mContext; @@ -82,9 +82,9 @@ public class WifiDiagnosticsTest { private static final int ALERT_REASON_CODE = 1; private static final byte[] ALERT_DATA = {0 , 4, 5}; /** Mock resource for fatal firmware alert list */ - private static final int[] FATAL_FW_ALART_LIST = {256, 257, 258}; + private static final int[] FATAL_FW_ALERT_LIST = {256, 257, 258}; /** Mock a non fatal firmware alert */ - private static final int NON_FATAL_FW_ALART = 0; + private static final int NON_FATAL_FW_ALERT = 0; private WifiNative.RingBufferStatus mFakeRbs; /** @@ -124,7 +124,7 @@ public class WifiDiagnosticsTest { resources.setInteger(R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb, LARGE_RING_BUFFER_SIZE_KB); resources.setIntArray(R.array.config_wifi_fatal_firmware_alert_error_code_list, - FATAL_FW_ALART_LIST); + FATAL_FW_ALERT_LIST); when(mContext.getResources()).thenReturn(resources); when(mWifiInjector.makeLog(anyString())).thenReturn(mLog); when(mWifiInjector.getJavaRuntime()).thenReturn(mJavaRuntime); @@ -896,7 +896,7 @@ public class WifiDiagnosticsTest { when(mBuildProperties.isUserBuild()).thenReturn(false); when(mWifiNative.flushRingBufferData()).thenReturn(true); /** captureAlertData with mock fatal firmware alert*/ - mWifiDiagnostics.captureAlertData(FATAL_FW_ALART_LIST[0], ALERT_DATA); + mWifiDiagnostics.captureAlertData(FATAL_FW_ALERT_LIST[0], ALERT_DATA); verify(mWifiNative).flushRingBufferData(); } @@ -906,7 +906,7 @@ public class WifiDiagnosticsTest { when(mBuildProperties.isUserBuild()).thenReturn(false); when(mWifiNative.flushRingBufferData()).thenReturn(true); /** captureAlertData with mock non fatal firmware alert*/ - mWifiDiagnostics.captureAlertData(NON_FATAL_FW_ALART, ALERT_DATA); + mWifiDiagnostics.captureAlertData(NON_FATAL_FW_ALERT, ALERT_DATA); verify(mWifiNative, never()).flushRingBufferData(); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java index 392466412e..2c73d09ae9 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java @@ -29,7 +29,7 @@ import org.mockito.MockitoAnnotations; /** Unit tests for {@link WifiInjector}. */ @SmallTest -public class WifiInjectorTest { +public class WifiInjectorTest extends WifiBaseTest { @Mock private Context mContext; private WifiInjector mInjector; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java index 7079a2d535..1cb432ef83 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java @@ -38,7 +38,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiConfigManager}. */ @SmallTest -public class WifiKeyStoreTest { +public class WifiKeyStoreTest extends WifiBaseTest { @Mock private WifiEnterpriseConfig mWifiEnterpriseConfig; @Mock private KeyStore mKeyStore; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java index 7c55228caf..023fddb177 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java @@ -27,14 +27,12 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiSsid; import android.os.Handler; import android.os.test.TestLooper; -import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.util.Pair; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.util.ArrayList; @@ -45,9 +43,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.WifiLastResortWatchdog}. */ @SmallTest -public class WifiLastResortWatchdogTest { - final ArgumentCaptor<OnPropertiesChangedListener> mOnPropertiesChangedListenerCaptor = - ArgumentCaptor.forClass(OnPropertiesChangedListener.class); +public class WifiLastResortWatchdogTest extends WifiBaseTest { WifiLastResortWatchdog mLastResortWatchdog; @Mock WifiInjector mWifiInjector; @Mock WifiMetrics mWifiMetrics; @@ -79,14 +75,13 @@ public class WifiLastResortWatchdogTest { when(mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled()).thenReturn(true); when(mDeviceConfigFacade.getAbnormalConnectionDurationMs()).thenReturn( DEFAULT_ABNORMAL_CONNECTION_DURATION_MS); + WifiThreadRunner wifiThreadRunner = new WifiThreadRunner(new Handler(mLooper.getLooper())); mLastResortWatchdog = new WifiLastResortWatchdog(mWifiInjector, mContext, mClock, - mWifiMetrics, mClientModeImpl, mLooper.getLooper(), mDeviceConfigFacade); + mWifiMetrics, mClientModeImpl, mLooper.getLooper(), mDeviceConfigFacade, + wifiThreadRunner); mLastResortWatchdog.setBugReportProbability(1); when(mClientModeImpl.getWifiInfo()).thenReturn(mWifiInfo); when(mWifiInfo.getSSID()).thenReturn(TEST_NETWORK_SSID); - when(mWifiInjector.getClientModeImplHandler()).thenReturn(mLastResortWatchdog.getHandler()); - verify(mDeviceConfigFacade).addOnPropertiesChangedListener(any(), - mOnPropertiesChangedListenerCaptor.capture()); } private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids, @@ -1520,8 +1515,9 @@ public class WifiLastResortWatchdogTest { verify(mWifiMetrics, times(1)).addCountToNumLastResortWatchdogBadDhcpNetworksTotal(3); verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggersWithBadDhcp(); - // set connection to ssids[0] + // set connection to ssids[0]/bssids[0] when(mWifiInfo.getSSID()).thenReturn(ssids[0]); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); // Simulate wifi connecting after triggering mLastResortWatchdog.connectedStateTransition(true); @@ -1859,8 +1855,9 @@ public class WifiLastResortWatchdogTest { // Simulate wifi disconnecting mLastResortWatchdog.connectedStateTransition(false); - // set connection to ssids[0] + // set connection to ssids[0]/bssids[0] when(mWifiInfo.getSSID()).thenReturn(ssids[0]); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); // Test another round, and this time successfully connect after restart trigger for (int i = 0; i < ssids.length; i++) { @@ -2073,8 +2070,9 @@ public class WifiLastResortWatchdogTest { verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggers(); // Age out network + candidates = new ArrayList<>(); for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE; i++) { - mLastResortWatchdog.updateAvailableNetworks(null); + mLastResortWatchdog.updateAvailableNetworks(candidates); } assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 0); @@ -2145,8 +2143,9 @@ public class WifiLastResortWatchdogTest { // Simulate wifi disconnecting mLastResortWatchdog.connectedStateTransition(false); - // set connection to ssids[0] + // set connection to ssids[0]/bssids[0] when(mWifiInfo.getSSID()).thenReturn(ssids[0]); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); // Test another round, and this time successfully connect after restart trigger for (int i = 0; i < ssids.length; i++) { @@ -2208,16 +2207,14 @@ public class WifiLastResortWatchdogTest { } /** - * Changes |mAbnormalConnectionDurationMs| to a new value, and then verify that a bugreport is + * Changes the abnormal connection duration to a new value, and then verify that a bugreport is * taken for a connection that takes longer than the new threshold. * @throws Exception */ @Test public void testGServicesSetDuration() throws Exception { final int testDurationMs = 10 * 1000; // 10 seconds - // changes the abnormal connection duration to |testDurationMs|. when(mDeviceConfigFacade.getAbnormalConnectionDurationMs()).thenReturn(testDurationMs); - mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null); // verifies that bugreport is taken for connections that take longer than |testDurationMs|. when(mClock.getElapsedSinceBootMillis()).thenReturn(1L); @@ -2233,14 +2230,12 @@ public class WifiLastResortWatchdogTest { /** * Verifies that bugreports are not triggered even when conditions are met after the - * |mAbnormalConnectionBugreportEnabled| flag is changed to false. + * applicable flag is changed to false. * @throws Exception */ @Test public void testGServicesFlagDisable() throws Exception { - // changes |mAbnormalConnectionBugreportEnabled| to false. when(mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled()).thenReturn(false); - mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null); // verifies that bugreports are not taken. when(mClock.getElapsedSinceBootMillis()).thenReturn(1L); @@ -2254,4 +2249,59 @@ public class WifiLastResortWatchdogTest { mLooper.dispatchAll(); verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString()); } + + /** + * Verifies that bugreports are not triggered if device connected back to a new BSSID + * after recovery. + */ + @Test + public void testNotTriggerBugreportIfConnectedToNewBssid() { + int associationRejections = 4; + int authenticationFailures = 3; + String[] ssids = {"\"test1\"", "\"test1\"", "\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55"}; + int[] frequencies = {2437, 5180, 5180}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {-60, -86, -50}; + boolean[] isEphemeral = {false, false, false}; + boolean[] hasEverConnected = {true, true, true}; + // Buffer potential candidates 1,2,& 3 + List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids, + bssids, frequencies, caps, levels, isEphemeral, hasEverConnected); + mLastResortWatchdog.updateAvailableNetworks(candidates); + + // Ensure new networks have zero'ed failure counts + for (int i = 0; i < ssids.length; i++) { + assertFailureCountEquals(bssids[i], 0, 0, 0); + } + + long timeAtFailure = 100; + long timeAtReconnect = 5000; + when(mClock.getElapsedSinceBootMillis()).thenReturn(timeAtFailure, timeAtReconnect); + + //Increment failure counts + for (int i = 0; i < associationRejections; i++) { + mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(ssids[1], bssids[1], + WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION); + } + for (int i = 0; i < authenticationFailures; i++) { + mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(ssids[2], bssids[2], + WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); + } + + // Verify watchdog has triggered a restart + verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggers(); + + // set connection to ssids[0]/bssids[0] + when(mWifiInfo.getSSID()).thenReturn(ssids[0]); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); + + // Simulate wifi connecting after triggering + mLastResortWatchdog.connectedStateTransition(true); + + // Verify takeBugReport is not called + mLooper.dispatchAll(); + verify(mWifiMetrics, never()).incrementNumLastResortWatchdogSuccesses(); + verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString()); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java index ecb72c08b2..d551680e4f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java @@ -30,7 +30,7 @@ import java.util.Random; * Unit tests for {@link com.android.server.wifi.WifiLinkLayerStats}. */ @SmallTest -public class WifiLinkLayerStatsTest { +public class WifiLinkLayerStatsTest extends WifiBaseTest { ExtendedWifiInfo mWifiInfo; WifiLinkLayerStats mWifiLinkLayerStats; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java index a40de55e99..396a853068 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java @@ -45,7 +45,7 @@ import java.io.StringWriter; /** Unit tests for {@link WifiLockManager}. */ @SmallTest -public class WifiLockManagerTest { +public class WifiLockManagerTest extends WifiBaseTest { private static final int DEFAULT_TEST_UID_1 = 52; private static final int DEFAULT_TEST_UID_2 = 53; @@ -669,7 +669,7 @@ public class WifiLockManagerTest { public void testForegroundAppAcquireLowLatencyScreenOn() throws Exception { // Set screen on, and app foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource); @@ -687,7 +687,7 @@ public class WifiLockManagerTest { public void testForegroundAppAcquireLowLatencyScreenOff() throws Exception { // Set screen off, and app is foreground mWifiLockManager.handleScreenStateChanged(false); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); assertTrue(mWifiLockManager.acquireWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource)); @@ -703,7 +703,7 @@ public class WifiLockManagerTest { public void testBackgroundAppAcquireLowLatencyScreenOn() throws Exception { // Set screen on, and app is background mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(false); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(false); assertTrue(mWifiLockManager.acquireWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource)); @@ -719,7 +719,7 @@ public class WifiLockManagerTest { public void testLatencyLockAcquireCauseLlEnableNew() throws Exception { // Set screen on, and app foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); @@ -739,7 +739,7 @@ public class WifiLockManagerTest { public void testLatencyLockAcquireCauseLL_enableLegacy() throws Exception { // Set screen on, and app foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_TX_POWER_LIMIT); @@ -758,7 +758,7 @@ public class WifiLockManagerTest { public void testLatencyLockReleaseCauseLlDisable() throws Exception { // Set screen on, and app foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); @@ -792,7 +792,7 @@ public class WifiLockManagerTest { // Set screen on, and app is foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); @@ -821,7 +821,7 @@ public class WifiLockManagerTest { // Set screen on, and app is foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); @@ -847,7 +847,7 @@ public class WifiLockManagerTest { public void testLatencyLockGoScreenOff() throws Exception { // Set screen on, app foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); @@ -881,7 +881,7 @@ public class WifiLockManagerTest { public void testLatencyLockGoBackground() throws Exception { // Initially, set screen on, app foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); @@ -920,7 +920,7 @@ public class WifiLockManagerTest { public void testLatencyLockGoForeground() throws Exception { // Initially, set screen on, and app background mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(false); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(false); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); // Make sure setLowLatencyMode() is successful @@ -959,7 +959,7 @@ public class WifiLockManagerTest { public void testLatencyHiPerfLocks() throws Exception { // Initially, set screen on, and app background mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(false); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(false); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); mWifiLockManager.updateWifiClientConnected(true); @@ -1070,7 +1070,7 @@ public class WifiLockManagerTest { when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true); when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true); mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); @@ -1246,7 +1246,7 @@ public class WifiLockManagerTest { public void testAcquireLockWhileConnectedDisconnect() throws Exception { // Set screen on, and app foreground mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource); mWifiLockManager.updateWifiClientConnected(false); @@ -1314,7 +1314,7 @@ public class WifiLockManagerTest { // Set condition for activation of low-latency (except connection to AP) mWifiLockManager.handleScreenStateChanged(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(INTERFACE_NAME)) .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index 73ac30f417..b3f1ed12e8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -69,7 +69,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.test.TestLooper; import android.provider.Settings; -import android.telephony.TelephonyManager; import android.util.Base64; import android.util.Pair; import android.util.SparseIntArray; @@ -129,7 +128,7 @@ import java.util.regex.Pattern; * Unit tests for {@link com.android.server.wifi.WifiMetrics}. */ @SmallTest -public class WifiMetricsTest { +public class WifiMetricsTest extends WifiBaseTest { WifiMetrics mWifiMetrics; WifiMetricsProto.WifiLog mDecodedProto; @@ -151,19 +150,16 @@ public class WifiMetricsTest { @Mock ExternalCallbackTracker<IOnWifiUsabilityStatsListener> mListenerTracker; @Mock WifiP2pMetrics mWifiP2pMetrics; @Mock DppMetrics mDppMetrics; - @Mock CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector; - @Mock CellularLinkLayerStats mCellularLinkLayerStats; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mDecodedProto = null; when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0); - when(mCellularLinkLayerStatsCollector.update()).thenReturn(mCellularLinkLayerStats); mTestLooper = new TestLooper(); mWifiMetrics = new WifiMetrics(mContext, mFacade, mClock, mTestLooper.getLooper(), new WifiAwareMetrics(mClock), new RttMetrics(mClock), mWifiPowerMetrics, - mWifiP2pMetrics, mDppMetrics, mCellularLinkLayerStatsCollector); + mWifiP2pMetrics, mDppMetrics); mWifiMetrics.setWifiConfigManager(mWcm); mWifiMetrics.setPasspointManager(mPpm); mWifiMetrics.setScoringParams(mScoringParams); @@ -383,8 +379,6 @@ public class WifiMetricsTest { private static final int NUM_PARTIAL_SCAN_RESULTS = 73; private static final int NUM_PNO_SCAN_ATTEMPTS = 20; private static final int NUM_PNO_SCAN_FAILED = 5; - private static final int NUM_PNO_SCAN_STARTED_OVER_OFFLOAD = 17; - private static final int NUM_PNO_SCAN_FAILED_OVER_OFFLOAD = 8; private static final int NUM_PNO_FOUND_NETWORK_EVENTS = 10; private static final int NUM_WPS_ATTEMPTS = 17; private static final int NUM_WPS_SUCCESS = 21; @@ -801,12 +795,6 @@ public class WifiMetricsTest { for (int i = 0; i < NUM_PNO_SCAN_FAILED; i++) { mWifiMetrics.incrementPnoScanFailedCount(); } - for (int i = 0; i < NUM_PNO_SCAN_STARTED_OVER_OFFLOAD; i++) { - mWifiMetrics.incrementPnoScanStartedOverOffloadCount(); - } - for (int i = 0; i < NUM_PNO_SCAN_FAILED_OVER_OFFLOAD; i++) { - mWifiMetrics.incrementPnoScanFailedOverOffloadCount(); - } for (int i = 0; i < NUM_PNO_FOUND_NETWORK_EVENTS; i++) { mWifiMetrics.incrementPnoFoundNetworkEventCount(); } @@ -1156,8 +1144,6 @@ public class WifiMetricsTest { assertNotNull(pno_metrics); assertEquals(NUM_PNO_SCAN_ATTEMPTS, pno_metrics.numPnoScanAttempts); assertEquals(NUM_PNO_SCAN_FAILED, pno_metrics.numPnoScanFailed); - assertEquals(NUM_PNO_SCAN_STARTED_OVER_OFFLOAD, pno_metrics.numPnoScanStartedOverOffload); - assertEquals(NUM_PNO_SCAN_FAILED_OVER_OFFLOAD, pno_metrics.numPnoScanFailedOverOffload); assertEquals(NUM_PNO_FOUND_NETWORK_EVENTS, pno_metrics.numPnoFoundNetworkEvents); for (ConnectToNetworkNotificationAndActionCount notificationCount @@ -1799,8 +1785,8 @@ public class WifiMetricsTest { {WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0}, {WifiMonitor.NETWORK_DISCONNECTION_EVENT, LOCAL_GEN, DEAUTH_REASON}, {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0}, - {ClientModeImpl.CMD_ASSOCIATED_BSSID, 0, 0}, - {ClientModeImpl.CMD_TARGET_BSSID, 0, 0}, + {WifiMonitor.ASSOCIATED_BSSID_EVENT, 0, 0}, + {WifiMonitor.TARGET_BSSID_EVENT, 0, 0}, {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0}, {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0} }; @@ -2869,16 +2855,6 @@ public class WifiMetricsTest { when(info.getBSSID()).thenReturn("Wifi"); when(info.getFrequency()).thenReturn(5745); - int signalStrengthDbm = -50; - int signalStrengthDb = -10; - boolean isSameRegisteredCell = true; - CellularLinkLayerStats cellularStats = new CellularLinkLayerStats(); - cellularStats.setIsSameRegisteredCell(isSameRegisteredCell); - cellularStats.setDataNetworkType(TelephonyManager.NETWORK_TYPE_LTE); - cellularStats.setSignalStrengthDbm(signalStrengthDbm); - cellularStats.setSignalStrengthDb(signalStrengthDb); - when(mCellularLinkLayerStatsCollector.update()).thenReturn(cellularStats); - WifiLinkLayerStats stats1 = nextRandomStats(new WifiLinkLayerStats()); WifiLinkLayerStats stats2 = nextRandomStats(stats1); mWifiMetrics.incrementWifiScoreCount(60); @@ -2931,14 +2907,6 @@ public class WifiMetricsTest { .stats[1].probeElapsedTimeSinceLastUpdateMs); assertEquals(-1, mDecodedProto.wifiUsabilityStatsList[0] .stats[0].probeElapsedTimeSinceLastUpdateMs); - assertEquals(WifiUsabilityStatsEntry.NETWORK_TYPE_LTE, - mDecodedProto.wifiUsabilityStatsList[0].stats[0].cellularDataNetworkType); - assertEquals(signalStrengthDbm, - mDecodedProto.wifiUsabilityStatsList[0].stats[0].cellularSignalStrengthDbm); - assertEquals(signalStrengthDb, - mDecodedProto.wifiUsabilityStatsList[0].stats[0].cellularSignalStrengthDb); - assertEquals(isSameRegisteredCell, - mDecodedProto.wifiUsabilityStatsList[0].stats[0].isSameRegisteredCell); assertEquals(DEVICE_MOBILITY_STATE_HIGH_MVMT, mDecodedProto.wifiUsabilityStatsList[1] .stats[mDecodedProto.wifiUsabilityStatsList[1].stats.length - 1] .deviceMobilityState); @@ -3237,13 +3205,6 @@ public class WifiMetricsTest { when(info.getRssi()).thenReturn(nextRandInt()); when(info.getLinkSpeed()).thenReturn(nextRandInt()); - CellularLinkLayerStats cellularStats = new CellularLinkLayerStats(); - cellularStats.setIsSameRegisteredCell(false); - cellularStats.setDataNetworkType(TelephonyManager.NETWORK_TYPE_UMTS); - cellularStats.setSignalStrengthDbm(-100); - cellularStats.setSignalStrengthDb(-20); - when(mCellularLinkLayerStatsCollector.update()).thenReturn(cellularStats); - WifiLinkLayerStats linkLayerStats = nextRandomStats(new WifiLinkLayerStats()); mWifiMetrics.updateWifiUsabilityStatsEntries(info, linkLayerStats); @@ -3257,10 +3218,6 @@ public class WifiMetricsTest { assertEquals(usabilityStats.getValue().getTimeStampMillis(), linkLayerStats.timeStampInMs); assertEquals(usabilityStats.getValue().getTotalRoamScanTimeMillis(), linkLayerStats.on_time_roam_scan); - assertEquals(usabilityStats.getValue().getCellularDataNetworkType(), - TelephonyManager.NETWORK_TYPE_UMTS); - assertEquals(usabilityStats.getValue().getCellularSignalStrengthDbm(), -100); - assertEquals(usabilityStats.getValue().getCellularSignalStrengthDb(), -20); } /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java index 5439b0ea5b..ba81cb3943 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java @@ -48,7 +48,7 @@ import org.mockito.ArgumentCaptor; * Unit tests for {@link com.android.server.wifi.WifiMonitor}. */ @SmallTest -public class WifiMonitorTest { +public class WifiMonitorTest extends WifiBaseTest { 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"}; @@ -403,14 +403,14 @@ public class WifiMonitorTest { @Test public void testBroadcastAssociatedBssidEvent() { mWifiMonitor.registerHandler( - WLAN_IFACE_NAME, ClientModeImpl.CMD_ASSOCIATED_BSSID, mHandlerSpy); + WLAN_IFACE_NAME, WifiMonitor.ASSOCIATED_BSSID_EVENT, 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(ClientModeImpl.CMD_ASSOCIATED_BSSID, messageCaptor.getValue().what); + assertEquals(WifiMonitor.ASSOCIATED_BSSID_EVENT, messageCaptor.getValue().what); assertEquals(bssid, (String) messageCaptor.getValue().obj); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java index 5298ba4c89..9b0f325042 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java @@ -36,7 +36,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiConfigStoreData}. */ @SmallTest -public class WifiMulticastLockManagerTest { +public class WifiMulticastLockManagerTest extends WifiBaseTest { private static final String WL_1_TAG = "Wakelock-1"; private static final String WL_2_TAG = "Wakelock-2"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java index c7a66e31a7..ccd81856b8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java @@ -37,8 +37,6 @@ import static org.mockito.Mockito.when; import android.app.test.MockAnswerUtil; import android.net.InterfaceConfiguration; -import android.net.wifi.IApInterface; -import android.net.wifi.IClientInterface; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiScanner; import android.os.Handler; @@ -53,6 +51,8 @@ import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener; import com.android.server.wifi.WifiNative.SupplicantDeathEventHandler; import com.android.server.wifi.WifiNative.VendorHalDeathEventHandler; import com.android.server.wifi.WifiNative.WificondDeathEventHandler; +import com.android.server.wifi.wificond.IApInterface; +import com.android.server.wifi.wificond.IClientInterface; import org.junit.After; import org.junit.Before; @@ -62,12 +62,15 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; +import java.util.HashSet; + /** * Unit tests for the interface management operations in * {@link com.android.server.wifi.WifiNative}. */ @SmallTest -public class WifiNativeInterfaceManagementTest { +public class WifiNativeInterfaceManagementTest extends WifiBaseTest { private static final String IFACE_NAME_0 = "mockWlan0"; private static final String IFACE_NAME_1 = "mockWlan1"; @@ -1277,6 +1280,85 @@ public class WifiNativeInterfaceManagementTest { mInOrder.verify(mWifiMetrics).incrementNumRadioModeChangeToDbs(); } + /** + * Verifies the switch of existing client interface in connectivity mode to scan mode. + */ + @Test + public void testSwitchClientInterfaceToScanMode() throws Exception { + executeAndValidateSetupClientInterface( + false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0, + mNetworkObserverCaptor0); + assertTrue(mWifiNative.switchClientInterfaceToScanMode(IFACE_NAME_0)); + + mInOrder.verify(mSupplicantStaIfaceHal).teardownIface(IFACE_NAME_0); + mInOrder.verify(mSupplicantStaIfaceHal).deregisterDeathHandler(); + mInOrder.verify(mSupplicantStaIfaceHal).terminate(); + mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0); + mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0); + } + + /** + * Verifies that a switch to scan mode when already in scan mode is rejected. + */ + @Test + public void testSwitchClientInterfaceToScanModeFailsWhenAlreadyInScanMode() throws Exception { + executeAndValidateSetupClientInterfaceForScan( + false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0, + mNetworkObserverCaptor0); + assertTrue(mWifiNative.switchClientInterfaceToScanMode(IFACE_NAME_0)); + } + + /** + * Verifies the switch of existing client interface in scan mode to connectivity mode. + */ + @Test + public void testSwitchClientInterfaceToConnectivityMode() throws Exception { + executeAndValidateSetupClientInterfaceForScan( + false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0, + mNetworkObserverCaptor0); + assertTrue(mWifiNative.switchClientInterfaceToConnectivityMode(IFACE_NAME_0)); + + mInOrder.verify(mSupplicantStaIfaceHal).isInitializationStarted(); + mInOrder.verify(mSupplicantStaIfaceHal).initialize(); + mInOrder.verify(mSupplicantStaIfaceHal).startDaemon(); + mInOrder.verify(mSupplicantStaIfaceHal).isInitializationComplete(); + mInOrder.verify(mSupplicantStaIfaceHal).registerDeathHandler(any()); + mInOrder.verify(mSupplicantStaIfaceHal).setupIface(IFACE_NAME_0); + mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0); + mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0); + } + + /** + * Verifies that a switch to connectivity mode when already in connectivity mode is rejected. + */ + @Test + public void testSwitchClientInterfaceToConnectivityModeFailsWhenAlreadyInConnectivityMode() + throws Exception { + executeAndValidateSetupClientInterface( + false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0, + mNetworkObserverCaptor0); + assertTrue(mWifiNative.switchClientInterfaceToConnectivityMode(IFACE_NAME_0)); + } + + /** + * Verifies the setup of two client interfaces. + */ + @Test + public void testSetupTwoClientInterfaces() throws Exception { + executeAndValidateSetupClientInterface( + false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0, + mNetworkObserverCaptor0); + assertEquals(new HashSet<>(Arrays.asList(IFACE_NAME_0)), + mWifiNative.getClientInterfaceNames()); + + executeAndValidateSetupClientInterface( + true, false, IFACE_NAME_1, mIfaceCallback1, mIfaceDestroyedListenerCaptor1, + mNetworkObserverCaptor1); + assertEquals(new HashSet<>(Arrays.asList(IFACE_NAME_0, IFACE_NAME_1)), + mWifiNative.getClientInterfaceNames()); + } + + private void executeAndValidateSetupClientInterface( boolean existingStaIface, boolean existingApIface, String ifaceName, @Mock WifiNative.InterfaceCallback callback, @@ -1382,7 +1464,7 @@ public class WifiNativeInterfaceManagementTest { mInOrder.verify(mWifiVendorHal).startVendorHal(); } mInOrder.verify(mWifiVendorHal).isVendorHalSupported(); - mInOrder.verify(mWifiVendorHal).createStaIface(eq(true), + mInOrder.verify(mWifiVendorHal).createStaIface(eq(false), destroyedListenerCaptor.capture()); mInOrder.verify(mWificondControl).setupInterfaceForClientMode(ifaceName); mInOrder.verify(mNwManagementService).registerObserver(networkObserverCaptor.capture()); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java index ab9d6f566a..1660c23cb9 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java @@ -50,7 +50,7 @@ import java.util.regex.Pattern; * Unit tests for {@link com.android.server.wifi.WifiNative}. */ @SmallTest -public class WifiNativeTest { +public class WifiNativeTest extends WifiBaseTest { private static final String WIFI_IFACE_NAME = "mockWlan"; private static final long FATE_REPORT_DRIVER_TIMESTAMP_USEC = 12345; private static final byte[] FATE_REPORT_FRAME_BYTES = new byte[] { diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java index 222c4953a8..c9e7db2f99 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java @@ -41,6 +41,7 @@ import android.net.MacAddress; import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkRequest; +import android.net.wifi.IActionListener; import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.INetworkRequestUserSelectionCallback; import android.net.wifi.ScanResult; @@ -50,6 +51,7 @@ import android.net.wifi.WifiNetworkSpecifier; import android.net.wifi.WifiScanner; import android.net.wifi.WifiScanner.ScanListener; import android.net.wifi.WifiScanner.ScanSettings; +import android.os.Binder; import android.os.IBinder; import android.os.Message; import android.os.Messenger; @@ -97,7 +99,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.WifiNetworkFactory}. */ @SmallTest -public class WifiNetworkFactoryTest { +public class WifiNetworkFactoryTest extends WifiBaseTest { private static final int TEST_NETWORK_ID_1 = 104; private static final int TEST_UID_1 = 10423; private static final int TEST_UID_2 = 10424; @@ -152,6 +154,8 @@ public class WifiNetworkFactoryTest { ArgumentCaptor.forClass(OnAlarmListener.class); ArgumentCaptor<ScanListener> mScanListenerArgumentCaptor = ArgumentCaptor.forClass(ScanListener.class); + ArgumentCaptor<IActionListener> mConnectListenerArgumentCaptor = + ArgumentCaptor.forClass(IActionListener.class); InOrder mInOrder; private WifiNetworkFactory mWifiNetworkFactory; @@ -175,7 +179,7 @@ public class WifiNetworkFactoryTest { .thenReturn(mConnectivityManager); when(mPackageManager.getNameForUid(TEST_UID_1)).thenReturn(TEST_PACKAGE_NAME_1); when(mPackageManager.getNameForUid(TEST_UID_2)).thenReturn(TEST_PACKAGE_NAME_2); - when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())) + when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), any())) .thenReturn(new ApplicationInfo()); when(mPackageManager.getApplicationLabel(any())).thenReturn(TEST_APP_NAME); when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1)) @@ -744,6 +748,49 @@ public class WifiNetworkFactoryTest { } /** + * Verify network specifier matching for a specifier containing a specific SSID match using + * 4 WPA_PSK + SAE transition scan results, each with unique SSID. + */ + @Test + public void testNetworkSpecifierMatchSuccessUsingLiteralSsidMatchForSaeTransitionScanResult() + throws Exception { + setupScanData(SCAN_RESULT_TYPE_WPA_PSK_SAE_TRANSITION, + TEST_SSID_1, TEST_SSID_2, TEST_SSID_3, TEST_SSID_4); + + // Setup network specifier for open networks. + PatternMatcher ssidPatternMatch = + new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL); + Pair<MacAddress, MacAddress> bssidPatternMatch = + Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier specifier = new WifiNetworkSpecifier( + ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1, + TEST_PACKAGE_NAME_1); + + mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); + mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); + + validateUiStartParams(true); + + mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback, + TEST_CALLBACK_IDENTIFIER); + + verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS); + + ArgumentCaptor<List<ScanResult>> matchedScanResultsCaptor = + ArgumentCaptor.forClass(List.class); + verify(mNetworkRequestMatchCallback).onMatch(matchedScanResultsCaptor.capture()); + + assertNotNull(matchedScanResultsCaptor.getValue()); + // We only expect 1 network match in this case. + validateScanResults(matchedScanResultsCaptor.getValue(), mTestScanDatas[0].getResults()[0]); + + verify(mWifiMetrics).incrementNetworkRequestApiMatchSizeHistogram( + matchedScanResultsCaptor.getValue().size()); + } + + /** * Verify network specifier matching for a specifier containing a Prefix SSID match using * 4 open scan results, each with unique SSID. */ @@ -1076,14 +1123,8 @@ public class WifiNetworkFactoryTest { eq(WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER)); verify(mClientModeImpl).disconnectCommand(); - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(messageCaptor.capture()); - - Message message = messageCaptor.getValue(); - assertNotNull(message); - - assertEquals(WifiManager.CONNECT_NETWORK, message.what); - assertEquals(TEST_NETWORK_ID_1, message.arg1); + verify(mClientModeImpl).connect(eq(null), eq(TEST_NETWORK_ID_1), any(Binder.class), + mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -1187,14 +1228,8 @@ public class WifiNetworkFactoryTest { verify(mWifiConfigManager, never()).addOrUpdateNetwork(any(), anyInt(), anyString()); verify(mClientModeImpl).disconnectCommand(); - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(messageCaptor.capture()); - - Message message = messageCaptor.getValue(); - assertNotNull(message); - - assertEquals(WifiManager.CONNECT_NETWORK, message.what); - assertEquals(TEST_NETWORK_ID_1, message.arg1); + verify(mClientModeImpl).connect(eq(null), eq(TEST_NETWORK_ID_1), any(Binder.class), + mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -1244,15 +1279,8 @@ public class WifiNetworkFactoryTest { eq(WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER)); verify(mClientModeImpl).disconnectCommand(); - // Verify connection message. - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(messageCaptor.capture()); - - Message message = messageCaptor.getValue(); - assertNotNull(message); - - assertEquals(WifiManager.CONNECT_NETWORK, message.what); - assertEquals(TEST_NETWORK_ID_1, message.arg1); + verify(mClientModeImpl).connect(eq(null), eq(TEST_NETWORK_ID_1), any(Binder.class), + mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -1303,15 +1331,8 @@ public class WifiNetworkFactoryTest { eq(WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER)); verify(mClientModeImpl).disconnectCommand(); - // Verify connection message. - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(messageCaptor.capture()); - - Message message = messageCaptor.getValue(); - assertNotNull(message); - - assertEquals(WifiManager.CONNECT_NETWORK, message.what); - assertEquals(TEST_NETWORK_ID_1, message.arg1); + verify(mClientModeImpl).connect(eq(null), eq(TEST_NETWORK_ID_1), any(Binder.class), + mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -1374,15 +1395,14 @@ public class WifiNetworkFactoryTest { */ @Test public void testNetworkSpecifierHandleConnectionTriggerFailure() throws Exception { - Messenger replyToMsgr = sendNetworkRequestAndSetupForConnectionStatus(); + sendNetworkRequestAndSetupForConnectionStatus(); // Send failure message beyond the retry limit to trigger the failure handling. for (int i = 0; i <= WifiNetworkFactory.USER_SELECTED_NETWORK_CONNECT_RETRY_MAX; i++) { - Message failureMsg = Message.obtain(); - failureMsg.what = WifiManager.CONNECT_NETWORK_FAILED; - replyToMsgr.send(failureMsg); - mLooper.dispatchAll(); + assertNotNull(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue()); + mConnectListenerArgumentCaptor.getValue().onFailure(WifiManager.ERROR); } + mLooper.dispatchAll(); mInOrder = inOrder(mAlarmManager, mClientModeImpl); validateConnectionRetryAttempts(); @@ -1611,6 +1631,7 @@ public class WifiNetworkFactoryTest { WifiConfiguration wcmNetwork = new WifiConfiguration(mSelectedNetwork); wcmNetwork.networkId = TEST_NETWORK_ID_1; wcmNetwork.creatorUid = TEST_UID_1; + wcmNetwork.creatorName = TEST_PACKAGE_NAME_1; wcmNetwork.fromWifiNetworkSpecifier = true; wcmNetwork.ephemeral = true; when(mWifiConfigManager.getConfiguredNetwork(mSelectedNetwork.configKey())) @@ -1618,7 +1639,8 @@ public class WifiNetworkFactoryTest { mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); // Verify that we triggered a disconnect. verify(mClientModeImpl, times(2)).disconnectCommand(); - verify(mWifiConfigManager).removeNetwork(TEST_NETWORK_ID_1, TEST_UID_1); + verify(mWifiConfigManager).removeNetwork( + TEST_NETWORK_ID_1, TEST_UID_1, TEST_PACKAGE_NAME_1); // Re-enable connectivity manager . verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false); } @@ -2124,7 +2146,8 @@ public class WifiNetworkFactoryTest { // Verify we did not trigger the match callback. verify(mNetworkRequestMatchCallback, never()).onMatch(anyList()); // Verify that we sent a connection attempt to ClientModeImpl - verify(mClientModeImpl).sendMessage(any()); + verify(mClientModeImpl).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); verify(mWifiMetrics).incrementNetworkRequestApiNumUserApprovalBypass(); } @@ -2170,7 +2193,8 @@ public class WifiNetworkFactoryTest { assertNotNull(matchedScanResultsCaptor.getValue()); validateScanResults(matchedScanResultsCaptor.getValue(), matchingScanResult); // Verify that we did not send a connection attempt to ClientModeImpl. - verify(mClientModeImpl, never()).sendMessage(any()); + verify(mClientModeImpl, never()).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -2211,7 +2235,8 @@ public class WifiNetworkFactoryTest { assertNotNull(matchedScanResultsCaptor.getValue()); validateScanResults(matchedScanResultsCaptor.getValue(), matchingScanResult); // Verify that we did not send a connection attempt to ClientModeImpl. - verify(mClientModeImpl, never()).sendMessage(any()); + verify(mClientModeImpl, never()).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -2250,7 +2275,8 @@ public class WifiNetworkFactoryTest { assertNotNull(matchedScanResultsCaptor.getValue()); validateScanResults(matchedScanResultsCaptor.getValue(), matchingScanResult); // Verify that we did not send a connection attempt to ClientModeImpl. - verify(mClientModeImpl, never()).sendMessage(any()); + verify(mClientModeImpl, never()).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -2293,7 +2319,8 @@ public class WifiNetworkFactoryTest { assertNotNull(matchedScanResultsCaptor.getValue()); validateScanResults(matchedScanResultsCaptor.getValue(), matchingScanResult); // Verify that we did not send a connection attempt to ClientModeImpl. - verify(mClientModeImpl, never()).sendMessage(any()); + verify(mClientModeImpl, never()).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -2335,7 +2362,8 @@ public class WifiNetworkFactoryTest { assertNotNull(matchedScanResultsCaptor.getValue()); validateScanResults(matchedScanResultsCaptor.getValue(), matchingScanResult); // Verify that we did not send a connection attempt to ClientModeImpl. - verify(mClientModeImpl, never()).sendMessage(any()); + verify(mClientModeImpl, never()).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -2404,7 +2432,8 @@ public class WifiNetworkFactoryTest { // Verify we did not trigger the match callback. verify(mNetworkRequestMatchCallback, never()).onMatch(anyList()); // Verify that we sent a connection attempt to ClientModeImpl - verify(mClientModeImpl).sendMessage(any()); + verify(mClientModeImpl).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); } /** @@ -2475,7 +2504,8 @@ public class WifiNetworkFactoryTest { // Verify we did not trigger the match callback. verify(mNetworkRequestMatchCallback, never()).onMatch(anyList()); // Verify that we sent a connection attempt to ClientModeImpl - verify(mClientModeImpl).sendMessage(any()); + verify(mClientModeImpl).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); verify(mWifiMetrics).incrementNetworkRequestApiNumUserApprovalBypass(); } @@ -2537,7 +2567,8 @@ public class WifiNetworkFactoryTest { // Verify we did not trigger the match callback. verify(mNetworkRequestMatchCallback, never()).onMatch(anyList()); // Verify that we sent a connection attempt to ClientModeImpl - verify(mClientModeImpl).sendMessage(any()); + verify(mClientModeImpl).connect(eq(null), anyInt(), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); verify(mWifiMetrics).incrementNetworkRequestApiNumUserApprovalBypass(); } @@ -2589,12 +2620,12 @@ public class WifiNetworkFactoryTest { matchedScanResultsCaptor.getValue().size()); } - private Messenger sendNetworkRequestAndSetupForConnectionStatus() throws RemoteException { - return sendNetworkRequestAndSetupForConnectionStatus(TEST_SSID_1); + private void sendNetworkRequestAndSetupForConnectionStatus() throws RemoteException { + sendNetworkRequestAndSetupForConnectionStatus(TEST_SSID_1); } // Helper method to setup the necessary pre-requisite steps for tracking connection status. - private Messenger sendNetworkRequestAndSetupForConnectionStatus(String targetSsid) + private void sendNetworkRequestAndSetupForConnectionStatus(String targetSsid) throws RemoteException { when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); @@ -2618,19 +2649,14 @@ public class WifiNetworkFactoryTest { verify(mWifiMetrics).incrementNetworkRequestApiNumApps(); verify(mClientModeImpl, atLeastOnce()).disconnectCommand(); - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl, atLeastOnce()).sendMessage(messageCaptor.capture()); - - Message message = messageCaptor.getValue(); - assertNotNull(message); + verify(mClientModeImpl, atLeastOnce()).connect(eq(null), eq(TEST_NETWORK_ID_1), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), anyInt()); // Start the connection timeout alarm. mInOrder.verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq((long) WifiNetworkFactory.NETWORK_CONNECTION_TIMEOUT_MS), any(), mConnectionTimeoutAlarmListenerArgumentCaptor.capture(), any()); assertNotNull(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue()); - - return message.replyTo; } private void sendNetworkRequestAndSetupForUserSelection() throws RemoteException { @@ -2739,6 +2765,7 @@ public class WifiNetworkFactoryTest { private static final int SCAN_RESULT_TYPE_OPEN = 0; private static final int SCAN_RESULT_TYPE_WPA_PSK = 1; private static final int SCAN_RESULT_TYPE_WPA_EAP = 2; + private static final int SCAN_RESULT_TYPE_WPA_PSK_SAE_TRANSITION = 3; private String getScanResultCapsForType(int scanResultType) { switch (scanResultType) { @@ -2751,6 +2778,8 @@ public class WifiNetworkFactoryTest { case SCAN_RESULT_TYPE_WPA_EAP: return WifiConfigurationTestUtil.getScanResultCapsForNetwork( WifiConfigurationTestUtil.createEapNetwork()); + case SCAN_RESULT_TYPE_WPA_PSK_SAE_TRANSITION: + return WifiConfigurationTestUtil.getScanResultCapsForWpa2Wpa3TransitionNetwork(); } fail("Invalid scan result type " + scanResultType); return ""; @@ -2805,11 +2834,9 @@ public class WifiNetworkFactoryTest { mConnectionTimeoutAlarmListenerArgumentCaptor.getValue()); // Trigger new connection. - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - mInOrder.verify(mClientModeImpl).sendMessage(messageCaptor.capture()); - Message message = messageCaptor.getValue(); - assertNotNull(message); - assertEquals(WifiManager.CONNECT_NETWORK, message.what); + mInOrder.verify(mClientModeImpl).connect(eq(null), eq(TEST_NETWORK_ID_1), + any(Binder.class), mConnectListenerArgumentCaptor.capture(), anyInt(), + anyInt()); // Start the new connection timeout alarm. mInOrder.verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java index bd7256a769..adb1be81c5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java @@ -60,7 +60,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.WifiNetworkSelector}. */ @SmallTest -public class WifiNetworkSelectorTest { +public class WifiNetworkSelectorTest extends WifiBaseTest { private static final int RSSI_BUMP = 1; private static final int DUMMY_EVALUATOR_ID_1 = -2; // lowest index @@ -1379,166 +1379,6 @@ public class WifiNetworkSelectorTest { } /** - * {@link WifiNetworkSelector#getFilteredScanDetailsForCarrierUnsavedNetworks()} should filter - * out networks that are not EAP after network selection is made. - * - * Expected behavior: return EAP networks only - */ - @Test - public void getfilterCarrierUnsavedNetworks_filtersForEapNetworks() { - String[] ssids = {"\"test1\"", "\"test2\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; - int[] freqs = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; - mDummyEvaluator.setEvaluatorToSelectCandidate(false); - - List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( - ssids, bssids, freqs, caps, levels, mClock); - HashSet<String> blacklist = new HashSet<>(); - - mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); - List<ScanDetail> expectedCarrierUnsavedNetworks = new ArrayList<>(); - expectedCarrierUnsavedNetworks.add(scanDetails.get(0)); - assertEquals("Expect carrier unsaved networks", - expectedCarrierUnsavedNetworks, - mWifiNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig)); - } - - /** - * {@link WifiNetworkSelector#getFilteredScanDetailsForCarrierUnsavedNetworks()} should filter - * out saved networks after network selection is made. This should return an empty list when - * there are no unsaved networks available. - * - * Expected behavior: return unsaved networks only. Return empty list if there are no unsaved - * networks. - */ - @Test - public void getfilterCarrierUnsavedNetworks_filtersOutSavedNetworks() { - String[] ssids = {"\"test1\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3"}; - int[] freqs = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP}; - int[] securities = {SECURITY_EAP}; - mDummyEvaluator.setEvaluatorToSelectCandidate(false); - - List<ScanDetail> unSavedScanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( - ssids, bssids, freqs, caps, levels, mClock); - HashSet<String> blacklist = new HashSet<>(); - - mWifiNetworkSelector.selectNetwork( - unSavedScanDetails, blacklist, mWifiInfo, false, true, false); - assertEquals("Expect carrier unsaved networks", - unSavedScanDetails, - mWifiNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig)); - - ScanDetailsAndWifiConfigs scanDetailsAndConfigs = - WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, - freqs, caps, levels, securities, mWifiConfigManager, mClock); - List<ScanDetail> savedScanDetails = scanDetailsAndConfigs.getScanDetails(); - - mWifiNetworkSelector.selectNetwork( - savedScanDetails, blacklist, mWifiInfo, false, true, false); - // Saved networks are filtered out. - assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig).isEmpty()); - } - - /** - * {@link WifiNetworkSelector#getFilteredScanDetailsForCarrierUnsavedNetworks()} should filter - * out bssid blacklisted networks. - * - * Expected behavior: do not return blacklisted network - */ - @Test - public void getfilterCarrierUnsavedNetworks_filtersOutBlacklistedNetworks() { - String[] ssids = {"\"test1\"", "\"test2\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; - int[] freqs = {2437, 5180}; - String[] caps = {"[EAP][ESS]", "[EAP]"}; - int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; - mDummyEvaluator.setEvaluatorToSelectCandidate(false); - - List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( - ssids, bssids, freqs, caps, levels, mClock); - HashSet<String> blacklist = new HashSet<>(); - blacklist.add(bssids[0]); - - mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); - List<ScanDetail> expectedCarrierUnsavedNetworks = new ArrayList<>(); - expectedCarrierUnsavedNetworks.add(scanDetails.get(1)); - assertEquals("Expect carrier unsaved networks", - expectedCarrierUnsavedNetworks, - mWifiNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig)); - } - - /** - * {@link WifiNetworkSelector#getFilteredScanDetailsForCarrierUnsavedNetworks()} should return - * empty list when there are no EAP encrypted networks after network selection is made. - * - * Expected behavior: return empty list - */ - @Test - public void getfilterCarrierUnsavedNetworks_returnsEmptyListWhenNoEAPNetworksPresent() { - String[] ssids = {"\"test1\"", "\"test2\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; - int[] freqs = {2437, 5180}; - String[] caps = {"[ESS]", "[WPA2-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; - mDummyEvaluator.setEvaluatorToSelectCandidate(false); - - List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( - ssids, bssids, freqs, caps, levels, mClock); - HashSet<String> blacklist = new HashSet<>(); - - mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); - assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig).isEmpty()); - } - - /** - * {@link WifiNetworkSelector#getFilteredScanDetailsForCarrierUnsavedNetworks()} should return - * empty list when there are no carrier networks after network selection is made. - * - * Expected behavior: return empty list - */ - @Test - public void getfilterCarrierUnsavedNetworks_returnsEmptyListWhenNoCarrierNetworksPresent() { - String[] ssids = {"\"test1\"", "\"test2\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; - int[] freqs = {2437, 5180}; - String[] caps = {"[EAP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; - mDummyEvaluator.setEvaluatorToSelectCandidate(false); - - List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( - ssids, bssids, freqs, caps, levels, mClock); - HashSet<String> blacklist = new HashSet<>(); - - mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); - - when(mCarrierNetworkConfig.isCarrierNetwork(any())).thenReturn(false); - assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig).isEmpty()); - } - - /** - * {@link WifiNetworkSelector#getFilteredScanDetailsForCarrierUnsavedNetworks()} should return - * empty list when no network selection has been made. - * - * Expected behavior: return empty list - */ - @Test - public void getfilterCarrierUnsavedNetworks_returnsEmptyListWhenNoNetworkSelectionMade() { - assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks( - mCarrierNetworkConfig).isEmpty()); - } - - /** * Test registerCandidateScorer. * * Just make sure it does not crash, for now. @@ -1640,12 +1480,7 @@ public class WifiNetworkSelectorTest { @Override public WifiCandidates.ScoredCandidate scoreCandidates( Collection<WifiCandidates.Candidate> group) { - return new WifiCandidates.ScoredCandidate(0, 0, null); - } - - @Override - public boolean userConnectChoiceOverrideWanted() { - return false; + return new WifiCandidates.ScoredCandidate(0, 0, false, null); } }; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java index 612fdf5757..0726514bb6 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java @@ -19,7 +19,6 @@ package com.android.server.wifi; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.AppOpsManager.OPSTR_CHANGE_WIFI_STATE; -import static android.app.AppOpsManager.OP_CHANGE_WIFI_STATE; import static android.app.Notification.EXTRA_BIG_TEXT; import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION; @@ -49,6 +48,8 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiScanner; +import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Handler; import android.os.UserHandle; import android.os.test.TestLooper; @@ -58,6 +59,7 @@ import com.android.internal.R; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion; import com.android.server.wifi.WifiNetworkSuggestionsManager.PerAppInfo; +import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.util.WifiPermissionsUtil; import org.junit.Before; @@ -80,12 +82,13 @@ import java.util.stream.Collectors; * Unit tests for {@link com.android.server.wifi.WifiNetworkSuggestionsManager}. */ @SmallTest -public class WifiNetworkSuggestionsManagerTest { +public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { private static final String TEST_PACKAGE_1 = "com.test12345"; private static final String TEST_PACKAGE_2 = "com.test54321"; private static final String TEST_APP_NAME_1 = "test12345"; private static final String TEST_APP_NAME_2 = "test54321"; private static final String TEST_BSSID = "00:11:22:33:44:55"; + private static final String TEST_FQDN = "FQDN"; private static final int TEST_UID_1 = 5667; private static final int TEST_UID_2 = 4537; @@ -100,8 +103,8 @@ public class WifiNetworkSuggestionsManagerTest { private @Mock WifiConfigStore mWifiConfigStore; private @Mock WifiConfigManager mWifiConfigManager; private @Mock NetworkSuggestionStoreData mNetworkSuggestionStoreData; - private @Mock ClientModeImpl mClientModeImpl; private @Mock WifiMetrics mWifiMetrics; + private @Mock PasspointManager mPasspointManager; private TestLooper mLooper; private ArgumentCaptor<AppOpsManager.OnOpChangedListener> mAppOpChangedListenerCaptor = ArgumentCaptor.forClass(AppOpsManager.OnOpChangedListener.class); @@ -126,7 +129,7 @@ public class WifiNetworkSuggestionsManagerTest { when(mWifiInjector.makeNetworkSuggestionStoreData(any())) .thenReturn(mNetworkSuggestionStoreData); when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade); - when(mWifiInjector.getClientModeImpl()).thenReturn(mClientModeImpl); + when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager); when(mFrameworkFacade.getBroadcast(any(), anyInt(), any(), anyInt())) .thenReturn(mock(PendingIntent.class)); when(mContext.getResources()).thenReturn(mResources); @@ -150,11 +153,11 @@ public class WifiNetworkSuggestionsManagerTest { when(mContext.getApplicationInfo()).thenReturn(ourAppInfo); // test app info ApplicationInfo appInfO1 = new ApplicationInfo(); - when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_1), eq(0), anyInt())) + when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_1), eq(0), any())) .thenReturn(appInfO1); when(mPackageManager.getApplicationLabel(appInfO1)).thenReturn(TEST_APP_NAME_1); ApplicationInfo appInfO2 = new ApplicationInfo(); - when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_2), eq(0), anyInt())) + when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_2), eq(0), any())) .thenReturn(appInfO2); when(mPackageManager.getApplicationLabel(appInfO2)).thenReturn(TEST_APP_NAME_2); @@ -183,11 +186,17 @@ public class WifiNetworkSuggestionsManagerTest { */ @Test public void testAddNetworkSuggestionsSuccess() { + PasspointConfiguration passpointConfiguration = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn(TEST_FQDN); + passpointConfiguration.setHomeSp(homeSp); + WifiConfiguration dummyConfiguration = new WifiConfiguration(); + dummyConfiguration.FQDN = TEST_FQDN; WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_2, + dummyConfiguration, passpointConfiguration, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -198,13 +207,16 @@ public class WifiNetworkSuggestionsManagerTest { new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion2); }}; - + when(mPasspointManager.addOrUpdateProvider(any(PasspointConfiguration.class), + anyInt(), anyString(), eq(true))).thenReturn(true); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1, TEST_PACKAGE_1)); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2, TEST_PACKAGE_2)); + verify(mPasspointManager).addOrUpdateProvider( + passpointConfiguration, TEST_UID_2, TEST_PACKAGE_2, true); Set<WifiNetworkSuggestion> allNetworkSuggestions = mWifiNetworkSuggestionsManager.getAllNetworkSuggestions(); @@ -228,11 +240,17 @@ public class WifiNetworkSuggestionsManagerTest { */ @Test public void testRemoveNetworkSuggestionsSuccess() { + PasspointConfiguration passpointConfiguration = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn(TEST_FQDN); + passpointConfiguration.setHomeSp(homeSp); + WifiConfiguration dummyConfiguration = new WifiConfiguration(); + dummyConfiguration.FQDN = TEST_FQDN; WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_2, + dummyConfiguration, passpointConfiguration, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -243,7 +261,8 @@ public class WifiNetworkSuggestionsManagerTest { new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion2); }}; - + when(mPasspointManager.addOrUpdateProvider(any(PasspointConfiguration.class), + anyInt(), anyString(), eq(true))).thenReturn(true); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1, TEST_PACKAGE_1)); @@ -258,6 +277,7 @@ public class WifiNetworkSuggestionsManagerTest { assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_UID_1, TEST_PACKAGE_2)); + verify(mPasspointManager).removeProvider(TEST_UID_2, false, TEST_FQDN); assertTrue(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions().isEmpty()); @@ -274,13 +294,20 @@ public class WifiNetworkSuggestionsManagerTest { */ @Test public void testRemoveAllNetworkSuggestionsSuccess() { + PasspointConfiguration passpointConfiguration = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn(TEST_FQDN); + passpointConfiguration.setHomeSp(homeSp); + WifiConfiguration dummyConfiguration = new WifiConfiguration(); + dummyConfiguration.FQDN = TEST_FQDN; WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_2, + dummyConfiguration, passpointConfiguration, false, false, TEST_UID_2, TEST_PACKAGE_2); + List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion1); @@ -290,6 +317,8 @@ public class WifiNetworkSuggestionsManagerTest { add(networkSuggestion2); }}; + when(mPasspointManager.addOrUpdateProvider(any(PasspointConfiguration.class), + anyInt(), anyString(), eq(true))).thenReturn(true); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1, TEST_PACKAGE_1)); @@ -304,6 +333,7 @@ public class WifiNetworkSuggestionsManagerTest { assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_UID_2, TEST_PACKAGE_2)); + verify(mPasspointManager).removeProvider(TEST_UID_2, false, TEST_FQDN); assertTrue(mWifiNetworkSuggestionsManager.getAllNetworkSuggestions().isEmpty()); } @@ -314,7 +344,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testReplaceNetworkSuggestionsSuccess() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -342,12 +372,12 @@ public class WifiNetworkSuggestionsManagerTest { } /** - * Verify that an attempt to modify networks that are already active is rejected. + * Verify that modify networks that are already active is allowed. */ @Test - public void testAddNetworkSuggestionsFailureOnInPlaceModification() { + public void testAddNetworkSuggestionsSuccessOnInPlaceModification() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -362,10 +392,13 @@ public class WifiNetworkSuggestionsManagerTest { networkSuggestion.wifiConfiguration.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED; - // Replace attempt should fail. - assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE, + // Replace attempt should success. + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1, TEST_PACKAGE_1)); + assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED, + mWifiNetworkSuggestionsManager + .get(TEST_PACKAGE_1).get(0).wifiConfiguration.meteredOverride); } /** @@ -377,7 +410,7 @@ public class WifiNetworkSuggestionsManagerTest { List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<>(); for (int i = 0; i < WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP; i++) { networkSuggestionList.add(new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1)); } // The first add should succeed. @@ -390,7 +423,7 @@ public class WifiNetworkSuggestionsManagerTest { networkSuggestionList = new ArrayList<>(); for (int i = 0; i < 3; i++) { networkSuggestionList.add(new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1)); } // The second add should fail. @@ -412,7 +445,7 @@ public class WifiNetworkSuggestionsManagerTest { networkSuggestionList = new ArrayList<>(); for (int i = 0; i < 2; i++) { networkSuggestionList.add(new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1)); } // This add should now succeed. @@ -427,10 +460,10 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testRemoveNetworkSuggestionsFailureOnInvalid() { WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -457,7 +490,7 @@ public class WifiNetworkSuggestionsManagerTest { public void testGetNetworkSuggestionsForScanDetailSuccessWithOneMatchForCarrierProvisioningApp() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -488,7 +521,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testGetNetworkSuggestionsForScanDetailSuccessWithOneMatch() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -517,11 +550,11 @@ public class WifiNetworkSuggestionsManagerTest { public void testGetNetworkSuggestionsForScanDetailSuccessWithMultipleMatch() { WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork(); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_1, + wifiConfiguration, null, false, false, TEST_UID_1, TEST_PACKAGE_1); // Reuse the same network credentials to ensure they both match. WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_2, + wifiConfiguration, null, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -564,7 +597,7 @@ public class WifiNetworkSuggestionsManagerTest { wifiConfiguration.BSSID = scanDetail.getBSSIDString(); WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_1, + wifiConfiguration, null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -594,11 +627,11 @@ public class WifiNetworkSuggestionsManagerTest { wifiConfiguration.BSSID = scanDetail.getBSSIDString(); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_1, + wifiConfiguration, null, false, false, TEST_UID_1, TEST_PACKAGE_1); // Reuse the same network credentials to ensure they both match. WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_2, + wifiConfiguration, null, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -641,11 +674,11 @@ public class WifiNetworkSuggestionsManagerTest { wifiConfiguration.BSSID = scanDetail.getBSSIDString(); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_1, + wifiConfiguration, null, false, false, TEST_UID_1, TEST_PACKAGE_1); // Reuse the same network credentials to ensure they both match. WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_1, + wifiConfiguration, null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = @@ -681,11 +714,11 @@ public class WifiNetworkSuggestionsManagerTest { wifiConfiguration2.BSSID = scanDetail.getBSSIDString(); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration1, false, false, TEST_UID_1, + wifiConfiguration1, null, false, false, TEST_UID_1, TEST_PACKAGE_1); // Reuse the same network credentials to ensure they both match. WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration2, false, false, TEST_UID_2, + wifiConfiguration2, null, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -735,7 +768,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testGetNetworkSuggestionsForScanDetailFailureOnAppNotApproved() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -758,7 +791,7 @@ public class WifiNetworkSuggestionsManagerTest { public void testGetNetworkSuggestionsForScanDetailFailureOnSuggestionRemoval() { WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork(); WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - wifiConfiguration, false, false, TEST_UID_1, + wifiConfiguration, null, false, false, TEST_UID_1, TEST_PACKAGE_1); ScanDetail scanDetail = createScanDetailForNetwork(wifiConfiguration); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -787,7 +820,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testGetNetworkSuggestionsForScanDetailFailureOnWrongNetwork() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -815,7 +848,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testOnNetworkConnectionSuccessWithOneMatch() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -827,9 +860,13 @@ public class WifiNetworkSuggestionsManagerTest { mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); // Simulate connecting to the network. + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); verify(mWifiMetrics).incrementNetworkSuggestionApiNumConnectSuccess(); @@ -849,7 +886,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testOnNetworkConnectionFailureWithOneMatch() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -859,11 +896,14 @@ public class WifiNetworkSuggestionsManagerTest { mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, TEST_PACKAGE_1)); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); - + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; // Simulate connecting to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_DHCP, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_DHCP, connectNetwork, TEST_BSSID); verify(mWifiMetrics).incrementNetworkSuggestionApiNumConnectFailure(); @@ -886,14 +926,14 @@ public class WifiNetworkSuggestionsManagerTest { public void testOnNetworkConnectionSuccessWithMultipleMatch() { WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork(); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration, true, false, TEST_UID_1, + wifiConfiguration, null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion1); }}; WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration, true, false, TEST_UID_2, + wifiConfiguration, null, true, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList2 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -909,9 +949,15 @@ public class WifiNetworkSuggestionsManagerTest { mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2); + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion1.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; + // Simulate connecting to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, wifiConfiguration, TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); verify(mWifiMetrics).incrementNetworkSuggestionApiNumConnectSuccess(); @@ -948,14 +994,14 @@ public class WifiNetworkSuggestionsManagerTest { WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork(); wifiConfiguration.BSSID = TEST_BSSID; WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration, true, false, TEST_UID_1, + wifiConfiguration, null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion1); }}; WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration, true, false, TEST_UID_2, + wifiConfiguration, null, true, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList2 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -970,10 +1016,14 @@ public class WifiNetworkSuggestionsManagerTest { TEST_PACKAGE_2)); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2); - + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion1.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; // Simulate connecting to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, wifiConfiguration, TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); verify(mWifiMetrics).incrementNetworkSuggestionApiNumConnectSuccess(); @@ -1011,14 +1061,14 @@ public class WifiNetworkSuggestionsManagerTest { WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); wifiConfiguration2.BSSID = TEST_BSSID; WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration1, true, false, TEST_UID_1, + wifiConfiguration1, null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion1); }}; WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration2, true, false, TEST_UID_2, + wifiConfiguration2, null, true, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList2 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1034,9 +1084,15 @@ public class WifiNetworkSuggestionsManagerTest { mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2); + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion1.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; + // Simulate connecting to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, wifiConfiguration1, TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); verify(mWifiMetrics).incrementNetworkSuggestionApiNumConnectSuccess(); @@ -1072,7 +1128,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testOnNetworkConnectionWhenAppNotApproved() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1084,10 +1140,15 @@ public class WifiNetworkSuggestionsManagerTest { verify(mWifiPermissionsUtil).checkNetworkCarrierProvisioningPermission(TEST_UID_1); assertFalse(mWifiNetworkSuggestionsManager.hasUserApprovedForApp(TEST_PACKAGE_1)); + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; + // Simulate connecting to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); // Verify no broadcast was sent out. mInorder.verify(mWifiPermissionsUtil, never()).enforceCanAccessScanResults( @@ -1106,7 +1167,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testOnNetworkConnectionWhenIsAppInteractionRequiredNotSet() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1118,10 +1179,15 @@ public class WifiNetworkSuggestionsManagerTest { verify(mWifiPermissionsUtil).checkNetworkCarrierProvisioningPermission(TEST_UID_1); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; + // Simulate connecting to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); // Verify no broadcast was sent out. mInorder.verify(mWifiPermissionsUtil, never()).enforceCanAccessScanResults( @@ -1140,7 +1206,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testOnNetworkConnectionWhenAppDoesNotHoldLocationPermission() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1155,10 +1221,15 @@ public class WifiNetworkSuggestionsManagerTest { doThrow(new SecurityException()) .when(mWifiPermissionsUtil).enforceCanAccessScanResults(TEST_PACKAGE_1, TEST_UID_1); + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; + // Simulate connecting to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); mInorder.verify(mWifiPermissionsUtil) .enforceCanAccessScanResults(TEST_PACKAGE_1, TEST_UID_1); @@ -1173,7 +1244,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testAddNetworkSuggestionsConfigStoreWrite() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = @@ -1215,7 +1286,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testRemoveNetworkSuggestionsConfigStoreWrite() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = @@ -1250,13 +1321,24 @@ public class WifiNetworkSuggestionsManagerTest { */ @Test public void testNetworkSuggestionsConfigStoreLoad() { + PasspointConfiguration passpointConfiguration = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn(TEST_FQDN); + passpointConfiguration.setHomeSp(homeSp); + WifiConfiguration dummyConfiguration = new WifiConfiguration(); + dummyConfiguration.FQDN = TEST_FQDN; PerAppInfo appInfo = new PerAppInfo(TEST_PACKAGE_1); appInfo.hasUserApproved = true; WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, + TEST_PACKAGE_1); + WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( + dummyConfiguration, passpointConfiguration, false, false, TEST_UID_1, TEST_PACKAGE_1); appInfo.extNetworkSuggestions.add( ExtendedWifiNetworkSuggestion.fromWns(networkSuggestion, appInfo)); + appInfo.extNetworkSuggestions.add( + ExtendedWifiNetworkSuggestion.fromWns(networkSuggestion1, appInfo)); mDataSource.fromDeserialized(new HashMap<String, PerAppInfo>() {{ put(TEST_PACKAGE_1, appInfo); }}); @@ -1266,6 +1348,7 @@ public class WifiNetworkSuggestionsManagerTest { Set<WifiNetworkSuggestion> expectedAllNetworkSuggestions = new HashSet<WifiNetworkSuggestion>() {{ add(networkSuggestion); + add(networkSuggestion1); }}; assertEquals(expectedAllNetworkSuggestions, allNetworkSuggestions); @@ -1278,6 +1361,18 @@ public class WifiNetworkSuggestionsManagerTest { add(networkSuggestion); }}; assertEquals(expectedMatchingNetworkSuggestions, matchingNetworkSuggestions); + + // Ensure we can lookup the passpoint network. + WifiConfiguration connectNetwork = WifiConfigurationTestUtil.createPasspointNetwork(); + connectNetwork.FQDN = TEST_FQDN; + Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestions = + mWifiNetworkSuggestionsManager + .getNetworkSuggestionsForWifiConfiguration(connectNetwork, null); + Set<ExtendedWifiNetworkSuggestion> expectedMatchingExtNetworkSuggestions = + new HashSet<ExtendedWifiNetworkSuggestion>() {{ + add(ExtendedWifiNetworkSuggestion.fromWns(networkSuggestion1, appInfo)); + }}; + assertEquals(expectedMatchingExtNetworkSuggestions, matchingExtNetworkSuggestions); } /** @@ -1289,7 +1384,7 @@ public class WifiNetworkSuggestionsManagerTest { PerAppInfo appInfo1 = new PerAppInfo(TEST_PACKAGE_1); appInfo1.hasUserApproved = true; WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); appInfo1.extNetworkSuggestions.add( ExtendedWifiNetworkSuggestion.fromWns(networkSuggestion1, appInfo1)); @@ -1303,7 +1398,7 @@ public class WifiNetworkSuggestionsManagerTest { PerAppInfo appInfo2 = new PerAppInfo(TEST_PACKAGE_2); appInfo2.hasUserApproved = true; WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); appInfo2.extNetworkSuggestions.add( ExtendedWifiNetworkSuggestion.fromWns(networkSuggestion2, appInfo2)); @@ -1335,102 +1430,73 @@ public class WifiNetworkSuggestionsManagerTest { } /** - * Verify that we don't disconnect from the network if the only network suggestion matching the - * connected network is removed when App doesn't have NetworkCarrierProvisioningPermission. - */ - @Test - public void - testRemoveNetworkSuggestionsMatchingConnectionSuccessWithOneMatchNoCarrierProvision() { - WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, - TEST_PACKAGE_1); - List<WifiNetworkSuggestion> networkSuggestionList = - new ArrayList<WifiNetworkSuggestion>() {{ - add(networkSuggestion); - }}; - when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1)) - .thenReturn(false); - assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, - mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, - TEST_PACKAGE_1)); - mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); - - // Simulate connecting to the network. - mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration, - TEST_BSSID); - - // Now remove the network suggestion and ensure we did not trigger a disconnect. - assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, - mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_UID_1, - TEST_PACKAGE_1)); - verify(mClientModeImpl, never()).disconnectCommand(); - } - - /** * Verify that we will disconnect from the network if the only network suggestion matching the - * connected network is removed when App has NetworkCarrierProvisioningPermission. + * connected network is removed. */ @Test public void - testRemoveNetworkSuggestionsMatchingConnectionSuccessWithOneMatchCarrierProvision() { + testRemoveNetworkSuggestionsMatchingConnectionSuccessWithOneMatch() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion); }}; - when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1)) - .thenReturn(true); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, TEST_PACKAGE_1)); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); - // Simulate connecting to the network. + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); // Now remove the network suggestion and ensure we did trigger a disconnect. assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_UID_1, TEST_PACKAGE_1)); - verify(mClientModeImpl).disconnectCommand(); + verify(mWifiConfigManager).removeSuggestionConfiguredNetwork( + networkSuggestion.wifiConfiguration.configKey()); } /** - * Verify that we will disconnect from network when App has NetworkCarrierProvisioningPermission - * and removed all its suggestions by remove empty list. + * Verify that we will disconnect from network when App removed all its suggestions by remove + * empty list. */ @Test public void - testRemoveAllNetworkSuggestionsMatchingConnectionSuccessWithOneMatchCarrierProvision() { + testRemoveAllNetworkSuggestionsMatchingConnectionSuccessWithOneMatch() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion); }}; - when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1)) - .thenReturn(true); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, TEST_PACKAGE_1)); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); - // Simulate connecting to the network. + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); // Now remove all network suggestion and ensure we did trigger a disconnect. assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_UID_1, TEST_PACKAGE_1)); - verify(mClientModeImpl).disconnectCommand(); + verify(mWifiConfigManager).removeSuggestionConfiguredNetwork( + networkSuggestion.wifiConfiguration.configKey()); } @@ -1442,14 +1508,14 @@ public class WifiNetworkSuggestionsManagerTest { public void testRemoveAppMatchingConnectionSuccessWithMultipleMatch() { WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork(); WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - wifiConfiguration, true, false, TEST_UID_1, + wifiConfiguration, null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ add(networkSuggestion1); }}; WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - wifiConfiguration, true, false, TEST_UID_2, + wifiConfiguration, null, true, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList2 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1466,16 +1532,22 @@ public class WifiNetworkSuggestionsManagerTest { mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2); // Simulate connecting to the network. + WifiConfiguration connectNetwork = + new WifiConfiguration(wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_NONE, wifiConfiguration, TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); // Now remove one of the apps and ensure we did not trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1); - verify(mClientModeImpl, never()).disconnectCommand(); + verify(mWifiConfigManager, never()).removeSuggestionConfiguredNetwork(anyString()); // Now remove the other app and ensure we trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_2); - verify(mClientModeImpl).disconnectCommand(); + verify(mWifiConfigManager).removeSuggestionConfiguredNetwork( + networkSuggestion2.wifiConfiguration.configKey()); } /** @@ -1485,7 +1557,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testRemoveAppNotMatchingConnectionSuccess() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1503,7 +1575,7 @@ public class WifiNetworkSuggestionsManagerTest { // Now remove the app and ensure we did not trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1); - verify(mClientModeImpl, never()).disconnectCommand(); + verify(mWifiConfigManager, never()).removeSuggestionConfiguredNetwork(anyString()); } /** @@ -1513,7 +1585,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testRemoveNetworkSuggestionsNotMatchingConnectionSuccessAfterConnectionFailure() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1523,11 +1595,14 @@ public class WifiNetworkSuggestionsManagerTest { mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, TEST_PACKAGE_1)); mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); - + WifiConfiguration connectNetwork = + new WifiConfiguration(networkSuggestion.wifiConfiguration); + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; // Simulate failing connection to the network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( - WifiMetrics.ConnectionEvent.FAILURE_DHCP, networkSuggestion.wifiConfiguration, - TEST_BSSID); + WifiMetrics.ConnectionEvent.FAILURE_DHCP, connectNetwork, TEST_BSSID); // Simulate connecting to some other network. mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( @@ -1536,7 +1611,7 @@ public class WifiNetworkSuggestionsManagerTest { // Now remove the app and ensure we did not trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1); - verify(mClientModeImpl, never()).disconnectCommand(); + verify(mWifiConfigManager, never()).removeSuggestionConfiguredNetwork(anyString()); } /** @@ -1546,10 +1621,10 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testAddRemoveNetworkSuggestionsStartStopAppOpsWatch() { WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_2, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -1595,7 +1670,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testAppOpsChangeAfterSuggestionsAdd() { WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1647,7 +1722,7 @@ public class WifiNetworkSuggestionsManagerTest { public void testAppOpsChangeAfterConfigStoreLoad() { PerAppInfo appInfo = new PerAppInfo(TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); appInfo.extNetworkSuggestions.add( ExtendedWifiNetworkSuggestion.fromWns(networkSuggestion, appInfo)); @@ -1694,7 +1769,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testAppOpsChangeWrongUid() { WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1737,10 +1812,10 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testRemoveApp() { WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_2, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -1803,10 +1878,10 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testClear() { WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_2, + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = @@ -1860,7 +1935,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testUserApprovalNotificationDismissalWhenGetScanResult() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1901,7 +1976,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testUserApprovalNotificationClickOnAllowWhenGetScanResult() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1947,7 +2022,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testUserApprovalNotificationClickOnDisallowWhenGetScanResult() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -1976,8 +2051,7 @@ public class WifiNetworkSuggestionsManagerTest { NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); // Ensure we turn off CHANGE_WIFI_STATE app-ops. verify(mAppOpsManager).setMode( - OP_CHANGE_WIFI_STATE, TEST_UID_1, - TEST_PACKAGE_1, MODE_IGNORED); + OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1, MODE_IGNORED); // Cancel the notification. verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); @@ -2017,7 +2091,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testUserApprovalNotificationWhilePreviousNotificationActive() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -2042,19 +2116,68 @@ public class WifiNetworkSuggestionsManagerTest { } /** + * Verify get network suggestion return the right result + * 1. App never suggested, should return empty list. + * 2. App has network suggestions, return all its suggestion. + * 3. App suggested and remove them all, should return empty list. + */ + @Test + public void testGetNetworkSuggestions() { + // test App never suggested. + List<WifiNetworkSuggestion> storedNetworkSuggestionListPerApp = + mWifiNetworkSuggestionsManager.get(TEST_PACKAGE_1); + assertEquals(storedNetworkSuggestionListPerApp.size(), 0); + + // App add network suggestions then get stored suggestions. + WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion( + WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, TEST_UID_1, + TEST_PACKAGE_1); + WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion( + WifiConfigurationTestUtil.createOweNetwork(), null, false, false, TEST_UID_1, + TEST_PACKAGE_1); + WifiNetworkSuggestion networkSuggestion3 = new WifiNetworkSuggestion( + WifiConfigurationTestUtil.createSaeNetwork(), null, false, false, TEST_UID_1, + TEST_PACKAGE_1); + WifiNetworkSuggestion networkSuggestion4 = new WifiNetworkSuggestion( + WifiConfigurationTestUtil.createPskNetwork(), null, false, false, TEST_UID_1, + TEST_PACKAGE_1); + List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<>(); + networkSuggestionList.add(networkSuggestion1); + networkSuggestionList.add(networkSuggestion2); + networkSuggestionList.add(networkSuggestion3); + networkSuggestionList.add(networkSuggestion4); + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, + TEST_PACKAGE_1)); + mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); + storedNetworkSuggestionListPerApp = + mWifiNetworkSuggestionsManager.get(TEST_PACKAGE_1); + assertEquals(new HashSet<>(networkSuggestionList), + new HashSet<>(storedNetworkSuggestionListPerApp)); + + // App remove all network suggestions, expect empty list. + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiNetworkSuggestionsManager.remove(new ArrayList<>(), TEST_UID_1, + TEST_PACKAGE_1)); + storedNetworkSuggestionListPerApp = + mWifiNetworkSuggestionsManager.get(TEST_PACKAGE_1); + assertEquals(storedNetworkSuggestionListPerApp.size(), 0); + } + + /** * Verify get hidden networks from All user approve network suggestions */ @Test public void testGetHiddenNetworks() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion hiddenNetworkSuggestion1 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createPskHiddenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createPskHiddenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); WifiNetworkSuggestion hiddenNetworkSuggestion2 = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createPskHiddenNetwork(), true, false, TEST_UID_2, + WifiConfigurationTestUtil.createPskHiddenNetwork(), null, true, false, TEST_UID_2, TEST_PACKAGE_2); List<WifiNetworkSuggestion> networkSuggestionList1 = new ArrayList<WifiNetworkSuggestion>() {{ @@ -2086,7 +2209,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testUserApprovalNotificationClickOnAllowDuringAddingSuggestions() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -2121,7 +2244,7 @@ public class WifiNetworkSuggestionsManagerTest { @Test public void testUserApprovalNotificationClickOnDisallowWhenAddSuggestions() { WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - WifiConfigurationTestUtil.createOpenNetwork(), true, false, TEST_UID_1, + WifiConfigurationTestUtil.createOpenNetwork(), null, true, false, TEST_UID_1, TEST_PACKAGE_1); List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<WifiNetworkSuggestion>() {{ @@ -2139,8 +2262,7 @@ public class WifiNetworkSuggestionsManagerTest { NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); // Ensure we turn off CHANGE_WIFI_STATE app-ops. verify(mAppOpsManager).setMode( - OP_CHANGE_WIFI_STATE, TEST_UID_1, - TEST_PACKAGE_1, MODE_IGNORED); + OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1, MODE_IGNORED); // Cancel the notification. verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); @@ -2170,6 +2292,57 @@ public class WifiNetworkSuggestionsManagerTest { } /** + * Verify a successful lookup of a single passpoint network suggestion matching the + * connected network. + * a) The corresponding network suggestion has the + * {@link WifiNetworkSuggestion#isAppInteractionRequired} flag set. + * b) The app holds location permission. + * This should trigger a broadcast to the app. + */ + @Test + public void testOnPasspointNetworkConnectionSuccessWithOneMatch() { + PasspointConfiguration passpointConfiguration = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn(TEST_FQDN); + passpointConfiguration.setHomeSp(homeSp); + WifiConfiguration dummyConfiguration = new WifiConfiguration(); + dummyConfiguration.FQDN = TEST_FQDN; + WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( + dummyConfiguration, passpointConfiguration, true, false, TEST_UID_1, + TEST_PACKAGE_1); + List<WifiNetworkSuggestion> networkSuggestionList = + new ArrayList<WifiNetworkSuggestion>() {{ + add(networkSuggestion); + }}; + when(mPasspointManager.addOrUpdateProvider(any(), anyInt(), anyString(), anyBoolean())) + .thenReturn(true); + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, + TEST_PACKAGE_1)); + + mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); + + // Simulate connecting to the network. + WifiConfiguration connectNetwork = WifiConfigurationTestUtil.createPasspointNetwork(); + connectNetwork.FQDN = TEST_FQDN; + connectNetwork.fromWifiNetworkSuggestion = true; + connectNetwork.ephemeral = true; + connectNetwork.creatorName = TEST_APP_NAME_1; + mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded( + WifiMetrics.ConnectionEvent.FAILURE_NONE, connectNetwork, TEST_BSSID); + + verify(mWifiMetrics).incrementNetworkSuggestionApiNumConnectSuccess(); + + // Verify that the correct broadcast was sent out. + mInorder.verify(mWifiPermissionsUtil) + .enforceCanAccessScanResults(TEST_PACKAGE_1, TEST_UID_1); + validatePostConnectionBroadcastSent(TEST_PACKAGE_1, networkSuggestion); + + // Verify no more broadcast were sent out. + mInorder.verifyNoMoreInteractions(); + } + + /** * Creates a scan detail corresponding to the provided network values. */ private ScanDetail createScanDetailForNetwork(WifiConfiguration configuration) { diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiPowerMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiPowerMetricsTest.java index b463171f0f..81ef3d65b5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiPowerMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiPowerMetricsTest.java @@ -40,7 +40,7 @@ import java.io.PrintWriter; * Unit tests for {@link com.android.server.wifi.WifiPowerMetrics}. */ @SmallTest -public class WifiPowerMetricsTest { +public class WifiPowerMetricsTest extends WifiBaseTest { @Mock IBatteryStats mBatteryStats; WifiPowerMetrics mWifiPowerMetrics; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardProtoTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardProtoTest.java index c7bf909777..d02ae2ed4c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardProtoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardProtoTest.java @@ -30,7 +30,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiScoreCardProto}. */ @SmallTest -public class WifiScoreCardProtoTest { +public class WifiScoreCardProtoTest extends WifiBaseTest { /** * Sets up for unit test diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java index 577f5bc65b..e649b28089 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java @@ -47,7 +47,7 @@ import java.util.Arrays; * Unit tests for {@link com.android.server.wifi.WifiScoreCard}. */ @SmallTest -public class WifiScoreCardTest { +public class WifiScoreCardTest extends WifiBaseTest { static final WifiSsid TEST_SSID_1 = WifiSsid.createFromAsciiEncoded("Joe's Place"); static final WifiSsid TEST_SSID_2 = WifiSsid.createFromAsciiEncoded("Poe's Raven"); @@ -494,4 +494,38 @@ public class WifiScoreCardTest { assertEquals(0, leftovers.length); } + /** + * Test that older items are evicted from memory. + */ + @Test + public void testOlderItemsShouldBeEvicted() throws Exception { + mWifiInfo.setRssi(-55); + mWifiInfo.setFrequency(5805); + mWifiInfo.setLinkSpeed(384); + mWifiScoreCard.installMemoryStore(mMemoryStore); + for (int i = 0; i < 256; i++) { + MacAddress bssid = MacAddress.fromBytes(new byte[]{2, 2, 2, 2, 2, (byte) i}); + mWifiInfo.setBSSID(bssid.toString()); + mWifiScoreCard.noteSignalPoll(mWifiInfo); + } + + verify(mMemoryStore, times(256)).read(any(), any()); + verify(mMemoryStore, atLeastOnce()).write(any(), any()); // Assumes target size < 256 + reset(mMemoryStore); + + for (int i = 256 - 3; i < 256; i++) { + MacAddress bssid = MacAddress.fromBytes(new byte[]{2, 2, 2, 2, 2, (byte) i}); + mWifiInfo.setBSSID(bssid.toString()); + mWifiScoreCard.noteSignalPoll(mWifiInfo); + } + verify(mMemoryStore, never()).read(any(), any()); // Assumes target size >= 3 + + for (int i = 0; i < 3; i++) { + MacAddress bssid = MacAddress.fromBytes(new byte[]{2, 2, 2, 2, 2, (byte) i}); + mWifiInfo.setBSSID(bssid.toString()); + mWifiScoreCard.noteSignalPoll(mWifiInfo); + } + verify(mMemoryStore, times(3)).read(any(), any()); // Assumes target size < 253 + } + } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java index 86de199c3e..2ed9e420f3 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java @@ -52,7 +52,7 @@ import java.io.PrintWriter; * Unit tests for {@link com.android.server.wifi.WifiScoreReport}. */ @SmallTest -public class WifiScoreReportTest { +public class WifiScoreReportTest extends WifiBaseTest { class FakeClock extends Clock { diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index bd8129a380..25e09777f4 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -17,9 +17,6 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.DEVICE_MOBILITY_STATE_STATIONARY; -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; @@ -32,14 +29,11 @@ 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_ENABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; import static android.net.wifi.WifiManager.WIFI_FEATURE_INFRA_5G; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; 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; @@ -47,18 +41,21 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.AdditionalAnswers.returnsSecondArg; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.ignoreStubs; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; @@ -66,6 +63,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -74,8 +72,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.app.ActivityManager; import android.app.AppOpsManager; -import android.app.admin.DeviceAdminInfo; -import android.app.admin.DevicePolicyManagerInternal; +import android.app.test.MockAnswerUtil; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -85,13 +82,20 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.res.Resources; +import android.net.MacAddress; +import android.net.NetworkStack; import android.net.Uri; +import android.net.wifi.IActionListener; import android.net.wifi.IDppCallback; +import android.net.wifi.ILocalOnlyHotspotCallback; import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.IOnWifiUsabilityStatsListener; +import android.net.wifi.IScanResultsListener; import android.net.wifi.ISoftApCallback; import android.net.wifi.ITrafficStateCallback; +import android.net.wifi.ITxPacketCountListener; import android.net.wifi.ScanResult; +import android.net.wifi.WifiClient; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiEnterpriseConfig; @@ -99,7 +103,9 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.SoftApCallback; +import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiSsid; +import android.net.wifi.WifiStackClient; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; @@ -110,9 +116,7 @@ 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; @@ -132,12 +136,14 @@ import com.android.server.wifi.util.WifiAsyncChannel; 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.ArgumentMatcher; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; @@ -154,7 +160,7 @@ import java.util.List; * Note: this is intended to build up over time and will not immediately cover the entire file. */ @SmallTest -public class WifiServiceImplTest { +public class WifiServiceImplTest extends WifiBaseTest { private static final String TAG = "WifiServiceImplTest"; private static final String SCAN_PACKAGE_NAME = "scanPackage"; @@ -174,6 +180,7 @@ public class WifiServiceImplTest { private static final String WIFI_IFACE_NAME2 = "wlan1"; private static final String TEST_COUNTRY_CODE = "US"; private static final String TEST_FACTORY_MAC = "10:22:34:56:78:92"; + private static final MacAddress TEST_FACTORY_MAC_ADDR = MacAddress.fromString(TEST_FACTORY_MAC); private static final String TEST_FQDN = "testfqdn"; private static final List<WifiConfiguration> TEST_WIFI_CONFIGURATION_LIST = Arrays.asList( WifiConfigurationTestUtil.generateWifiConfig( @@ -193,17 +200,16 @@ public class WifiServiceImplTest { private WifiServiceImpl mWifiServiceImpl; private TestLooper mLooper; private PowerManager mPowerManager; - private Handler mHandler; - private Handler mHandlerSpyForCmiRunWithScissors; - private Messenger mAppMessenger; private int mPid; private int mPid2 = Process.myPid(); private OsuProvider mOsuProvider; private SoftApCallback mStateMachineSoftApCallback; + private SoftApCallback mLohsApCallback; + private String mLohsInterfaceName; private ApplicationInfo mApplicationInfo; private static final String DPP_URI = "DPP:some_dpp_uri"; - final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = + private final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); final ArgumentCaptor<IntentFilter> mIntentFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class); @@ -217,7 +223,6 @@ public class WifiServiceImplTest { @Mock WifiInjector mWifiInjector; @Mock WifiCountryCode mWifiCountryCode; @Mock Clock mClock; - @Mock WifiController mWifiController; @Mock WifiTrafficPoller mWifiTrafficPoller; @Mock ClientModeImpl mClientModeImpl; @Mock ActiveModeWarden mActiveModeWarden; @@ -252,7 +257,6 @@ public class WifiServiceImplTest { @Mock ITrafficStateCallback mTrafficStateCallback; @Mock INetworkRequestMatchCallback mNetworkRequestMatchCallback; @Mock WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; - @Mock DevicePolicyManagerInternal mDevicePolicyManagerInternal; @Mock TelephonyManager mTelephonyManager; @Mock IOnWifiUsabilityStatsListener mOnWifiUsabilityStatsListener; @Mock WifiConfigManager mWifiConfigManager; @@ -260,75 +264,15 @@ public class WifiServiceImplTest { @Mock WifiScoreCard mWifiScoreCard; @Mock PasspointManager mPasspointManager; @Mock IDppCallback mDppCallback; + @Mock SarManager mSarManager; + @Mock ILocalOnlyHotspotCallback mLohsCallback; + @Mock IScanResultsListener mClientScanResultsListener; @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); - } - - private Message sendMessageSynchronously(Message request) { - return mChannel.sendMessageSynchronously(request); - } - - private void sendMessage(Message request) { - mChannel.sendMessage(request); - } - } - @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mLooper = new TestLooper(); - mHandler = spy(new Handler(mLooper.getLooper())); - mAppMessenger = new Messenger(mHandler); mAsyncChannel = spy(new AsyncChannel()); mApplicationInfo = new ApplicationInfo(); mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; @@ -338,19 +282,20 @@ public class WifiServiceImplTest { when(mRequestInfo2.getPid()).thenReturn(mPid2); when(mWifiInjector.getUserManager()).thenReturn(mUserManager); when(mWifiInjector.getWifiCountryCode()).thenReturn(mWifiCountryCode); - when(mWifiInjector.getWifiController()).thenReturn(mWifiController); when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); when(mWifiInjector.getClientModeImpl()).thenReturn(mClientModeImpl); when(mClientModeImpl.syncInitialize(any())).thenReturn(true); when(mClientModeImpl.getHandler()).thenReturn(new Handler()); when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); - when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mHandlerThread); + when(mWifiInjector.getAsyncChannelHandlerThread()).thenReturn(mHandlerThread); + when(mWifiInjector.getWifiHandlerThread()).thenReturn(mHandlerThread); + when(mHandlerThread.getThreadHandler()).thenReturn(new Handler(mLooper.getLooper())); when(mWifiInjector.getPowerProfile()).thenReturn(mPowerProfile); when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); when(mContext.getResources()).thenReturn(mResources); when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())) + when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), any())) .thenReturn(mApplicationInfo); when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore); doNothing().when(mFrameworkFacade).registerContentObserver(eq(mContext), any(), @@ -364,8 +309,6 @@ public class WifiServiceImplTest { WifiAsyncChannel wifiAsyncChannel = new WifiAsyncChannel("WifiServiceImplTest"); wifiAsyncChannel.setWifiLog(mLog); when(mFrameworkFacade.makeWifiAsyncChannel(anyString())).thenReturn(wifiAsyncChannel); - when(mWifiPermissionsWrapper.getDevicePolicyManagerInternal()) - .thenReturn(mDevicePolicyManagerInternal); when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade); when(mWifiInjector.getWifiLockManager()).thenReturn(mLockManager); when(mWifiInjector.getWifiMulticastLockManager()).thenReturn(mWifiMulticastLockManager); @@ -385,10 +328,11 @@ public class WifiServiceImplTest { when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager); when(mClientModeImpl.getWifiScoreReport()).thenReturn(mWifiScoreReport); when(mWifiInjector.getWifiScoreCard()).thenReturn(mWifiScoreCard); + when(mWifiInjector.getSarManager()).thenReturn(mSarManager); + when(mWifiInjector.getWifiThreadRunner()) + .thenReturn(new WifiThreadRunner(new Handler(mLooper.getLooper()))); when(mClientModeImpl.syncStartSubscriptionProvisioning(anyInt(), any(OsuProvider.class), any(IProvisioningCallback.class), any())).thenReturn(true); - when(mPackageManager.hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(true); // Create an OSU provider that can be provisioned via an open OSU AP mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true); when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME); @@ -400,14 +344,12 @@ public class WifiServiceImplTest { anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_MANAGED_PROVISIONING), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); when(mScanRequestProxy.startScan(anyInt(), anyString())).thenReturn(true); + when(mLohsCallback.asBinder()).thenReturn(mock(IBinder.class)); - ArgumentCaptor<SoftApCallback> softApCallbackCaptor = - ArgumentCaptor.forClass(SoftApCallback.class); - mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel); - verify(mActiveModeWarden).registerSoftApCallback(softApCallbackCaptor.capture()); - mStateMachineSoftApCallback = softApCallbackCaptor.getValue(); - mWifiServiceImpl.setWifiHandlerLogForTest(mLog); + mWifiServiceImpl = makeWifiServiceImpl(); mDppCallback = new IDppCallback() { @Override public void onSuccessConfigReceived(int newNetworkId) throws RemoteException { @@ -436,36 +378,58 @@ public class WifiServiceImplTest { }; } - private WifiAsyncChannelTester verifyAsyncChannelHalfConnected() throws RemoteException { - WifiAsyncChannelTester channelTester = new WifiAsyncChannelTester(mWifiInjector); - Handler handler = mock(Handler.class); - TestLooper looper = new TestLooper(); - channelTester.connect(looper.getLooper(), - mWifiServiceImpl.getWifiServiceMessenger(TEST_PACKAGE_NAME), handler); + /** + * Called after each test + */ + @After + public void cleanup() { + validateMockitoUsage(); + } + + private WifiServiceImpl makeWifiServiceImpl() { + reset(mActiveModeWarden); + WifiServiceImpl wifiServiceImpl = + new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel); + ArgumentCaptor<SoftApCallback> softApCallbackCaptor = + ArgumentCaptor.forClass(SoftApCallback.class); + verify(mActiveModeWarden).registerSoftApCallback(softApCallbackCaptor.capture()); + mStateMachineSoftApCallback = softApCallbackCaptor.getValue(); + ArgumentCaptor<SoftApCallback> lohsCallbackCaptor = + ArgumentCaptor.forClass(SoftApCallback.class); + mLohsInterfaceName = WIFI_IFACE_NAME; + verify(mActiveModeWarden).registerLohsCallback(lohsCallbackCaptor.capture()); + mLohsApCallback = lohsCallbackCaptor.getValue(); mLooper.dispatchAll(); - assertEquals("AsyncChannel must be half connected", - WifiAsyncChannelTester.CHANNEL_STATE_HALF_CONNECTED, - channelTester.getChannelState()); - return channelTester; + return wifiServiceImpl; } - /** - * Verifies that any operations on WifiServiceImpl without setting up the ClientModeImpl - * channel would fail. - */ - @Test - public void testRemoveNetworkUnknown() { - assertFalse(mWifiServiceImpl.removeNetwork(-1, TEST_PACKAGE_NAME)); - verify(mClientModeImpl, never()).syncRemoveNetwork(any(), anyInt()); + private WifiServiceImpl makeWifiServiceImplWithMockRunnerWhichTimesOut() { + WifiThreadRunner mockRunner = mock(WifiThreadRunner.class); + when(mockRunner.call(any(), any())).then(returnsSecondArg()); + when(mockRunner.call(any(), any(int.class))).then(returnsSecondArg()); + when(mockRunner.call(any(), any(boolean.class))).then(returnsSecondArg()); + when(mockRunner.post(any())).thenReturn(false); + + when(mWifiInjector.getWifiThreadRunner()).thenReturn(mockRunner); + return makeWifiServiceImpl(); } /** - * Tests whether we're able to set up an async channel connection with WifiServiceImpl. - * This is the path used by some WifiManager public API calls. + * Test that REMOVE_NETWORK returns failure to public API when WifiConfigManager returns + * failure. */ @Test - public void testAsyncChannelHalfConnected() throws RemoteException { - verifyAsyncChannelHalfConnected(); + public void testRemoveNetworkFailureAppBelowQSdk() { + doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) + .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); + when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), + eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true); + when(mWifiConfigManager.removeNetwork(anyInt(), anyInt(), anyString())).thenReturn(false); + + mLooper.startAutoDispatch(); + boolean succeeded = mWifiServiceImpl.removeNetwork(0, TEST_PACKAGE_NAME); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + assertFalse(succeeded); } /** @@ -487,9 +451,9 @@ public class WifiServiceImplTest { */ @Test public void testDumpNullArgs() { - setupClientModeImplHandlerForRunWithScissors(); - + mLooper.startAutoDispatch(); mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null); + mLooper.stopAutoDispatchAndIgnoreExceptions(); } /** @@ -508,16 +472,13 @@ public class WifiServiceImplTest { */ @Test public void testWifiScoreReportDump() { - setupClientModeImplHandlerForRunWithScissors(); - + mLooper.startAutoDispatch(); mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null); + mLooper.stopAutoDispatchAndIgnoreExceptions(); - InOrder inOrder = inOrder(mClientModeImpl, mHandlerSpyForCmiRunWithScissors, - mWifiScoreReport); + InOrder inOrder = inOrder(mClientModeImpl, mWifiScoreReport); inOrder.verify(mClientModeImpl).updateLinkLayerStatsRssiAndScoreReport(); - inOrder.verify(mHandlerSpyForCmiRunWithScissors, atLeastOnce()) - .runWithScissors(any(), anyLong()); inOrder.verify(mWifiScoreReport).dump(any(), any(), any()); } @@ -542,7 +503,7 @@ public class WifiServiceImplTest { * Verify that metrics is incremented correctly for normal Apps targeting pre-Q. */ @Test - public void testSetWifiEnabledMetricsNormalAppBelowQSDK() throws Exception { + public void testSetWifiEnabledMetricsNormalAppBelowQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -561,7 +522,7 @@ public class WifiServiceImplTest { * Verify that metrics is not incremented by apps targeting Q SDK. */ @Test - public void testSetWifiEnabledMetricsNormalAppTargetingQSDKNoIncrement() throws Exception { + public void testSetWifiEnabledMetricsNormalAppTargetingQSdkNoIncrement() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -584,7 +545,7 @@ public class WifiServiceImplTest { when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** @@ -598,34 +559,33 @@ public class WifiServiceImplTest { when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** * Verify that wifi can be enabled by the DO apps targeting Q SDK. */ @Test - public void testSetWifiEnabledSuccessForDOAppsTargetingQSDK() throws Exception { + public void testSetWifiEnabledSuccessForDOAppsTargetingQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false); - when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy( - Process.myUid(), DeviceAdminInfo.USES_POLICY_DEVICE_OWNER)) + when(mWifiPermissionsUtil.isDeviceOwner(Binder.getCallingUid(), TEST_PACKAGE_NAME)) .thenReturn(true); when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** * Verify that wifi can be enabled by the system apps targeting Q SDK. */ @Test - public void testSetWifiEnabledSuccessForSystemAppsTargetingQSDK() throws Exception { + public void testSetWifiEnabledSuccessForSystemAppsTargetingQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -636,14 +596,14 @@ public class WifiServiceImplTest { when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** * Verify that wifi can be enabled by the apps targeting pre-Q SDK. */ @Test - public void testSetWifiEnabledSuccessForAppsTargetingBelowQSDK() throws Exception { + public void testSetWifiEnabledSuccessForAppsTargetingBelowQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -653,28 +613,14 @@ public class WifiServiceImplTest { when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); - } - - /** - * Verify that wifi is not enabled when wificontroller is not started. - */ - @Test - public void testSetWifiEnabledFailureWhenInCryptDebounce() throws Exception { - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true); - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); - when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); - when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); - assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verifyZeroInteractions(mWifiController); + verify(mActiveModeWarden).wifiToggled(); } /** * Verify that wifi cannot be enabled by the apps targeting Q SDK. */ @Test - public void testSetWifiEnabledFailureForAppsTargetingQSDK() throws Exception { + public void testSetWifiEnabledFailureForAppsTargetingQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -684,7 +630,7 @@ public class WifiServiceImplTest { when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); } /** @@ -703,7 +649,7 @@ public class WifiServiceImplTest { } catch (SecurityException e) { } - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); } /** @@ -719,7 +665,7 @@ public class WifiServiceImplTest { when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true); - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); } /** @@ -735,7 +681,7 @@ public class WifiServiceImplTest { .thenReturn(PackageManager.PERMISSION_GRANTED); assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** @@ -755,7 +701,25 @@ public class WifiServiceImplTest { .thenReturn(PackageManager.PERMISSION_DENIED); assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); + } + + /** + * Helper to verify registering for state changes. + */ + private void verifyApRegistration() { + assertNotNull(mLohsApCallback); + } + + /** + * Helper to emulate local-only hotspot state changes. + * + * Must call verifyApRegistration first. + */ + private void changeLohsState(int apState, int previousState, int error) { + // TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + // apState, previousState, error, WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); + mLohsApCallback.onStateChanged(apState, error); } /** @@ -767,12 +731,8 @@ public class WifiServiceImplTest { when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); - - TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_ENABLING, SAP_START_FAILURE_GENERAL, - WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); + verifyApRegistration(); + mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_ENABLED, 0); when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); when(mContext.checkPermission( @@ -780,7 +740,7 @@ public class WifiServiceImplTest { .thenReturn(PackageManager.PERMISSION_GRANTED); when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** @@ -795,12 +755,8 @@ public class WifiServiceImplTest { when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); - - TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_ENABLING, SAP_START_FAILURE_GENERAL, - WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); + verifyApRegistration(); + mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_ENABLED, 0); when(mContext.checkPermission( eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) @@ -808,7 +764,7 @@ public class WifiServiceImplTest { when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); verify(mSettingsStore, never()).handleWifiToggled(anyBoolean()); - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); } @@ -821,7 +777,7 @@ public class WifiServiceImplTest { when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); } /** @@ -849,7 +805,7 @@ public class WifiServiceImplTest { anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** @@ -862,34 +818,33 @@ public class WifiServiceImplTest { anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** * Verify that wifi can be disabled by the PO apps targeting Q SDK. */ @Test - public void testSetWifiDisabledSuccessForPOAppsTargetingQSDK() throws Exception { + public void testSetWifiDisabledSuccessForPOAppsTargetingQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(false); - when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy( - Process.myUid(), DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) + when(mWifiPermissionsUtil.isProfileOwner(Binder.getCallingUid(), TEST_PACKAGE_NAME)) .thenReturn(true); when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true); when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } /** * Verify that wifi can be disabled by the system apps targeting Q SDK. */ @Test - public void testSetWifiDisabledSuccessForSystemAppsTargetingQSDK() throws Exception { + public void testSetWifiDisabledSuccessForSystemAppsTargetingQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -900,7 +855,7 @@ public class WifiServiceImplTest { when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden).wifiToggled(); } @@ -908,7 +863,7 @@ public class WifiServiceImplTest { * Verify that wifi can be disabled by the apps targeting pre-Q SDK. */ @Test - public void testSetWifiDisabledSuccessForAppsTargetingBelowQSDK() throws Exception { + public void testSetWifiDisabledSuccessForAppsTargetingBelowQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -918,28 +873,14 @@ public class WifiServiceImplTest { when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); - } - - /** - * Verify that wifi is not disabled when wificontroller is not started. - */ - @Test - public void testSetWifiDisabledFailureWhenInCryptDebounce() throws Exception { - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true); - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); - when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(false); - when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); - assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verifyZeroInteractions(mWifiController); + verify(mActiveModeWarden).wifiToggled(); } /** * Verify that wifi cannot be disabled by the apps targeting Q SDK. */ @Test - public void testSetWifiDisabledFailureForAppsTargetingQSDK() throws Exception { + public void testSetWifiDisabledFailureForAppsTargetingQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), @@ -949,7 +890,7 @@ public class WifiServiceImplTest { when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); } /** @@ -961,7 +902,7 @@ public class WifiServiceImplTest { anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false)); - verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); + verify(mActiveModeWarden, never()).wifiToggled(); } /** @@ -981,11 +922,9 @@ public class WifiServiceImplTest { /** * Ensure unpermitted callers cannot write the SoftApConfiguration. - * - * @throws SecurityException */ @Test - public void testSetWifiApConfigurationNotSavedWithoutPermission() { + public void testSetWifiApConfigurationNotSavedWithoutPermission() throws Exception { when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false); WifiConfiguration apConfig = new WifiConfiguration(); try { @@ -998,7 +937,7 @@ public class WifiServiceImplTest { * Ensure softap config is written when the caller has the correct permission. */ @Test - public void testSetWifiApConfigurationSuccess() { + public void testSetWifiApConfigurationSuccess() throws Exception { when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); WifiConfiguration apConfig = createValidSoftApConfiguration(); @@ -1012,7 +951,7 @@ public class WifiServiceImplTest { * Ensure that a null config does not overwrite the saved ap config. */ @Test - public void testSetWifiApConfigurationNullConfigNotSaved() { + public void testSetWifiApConfigurationNullConfigNotSaved() throws Exception { when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); assertFalse(mWifiServiceImpl.setWifiApConfiguration(null, TEST_PACKAGE_NAME)); verify(mWifiApConfigStore, never()).setApConfiguration(isNull(WifiConfiguration.class)); @@ -1022,7 +961,7 @@ public class WifiServiceImplTest { * Ensure that an invalid config does not overwrite the saved ap config. */ @Test - public void testSetWifiApConfigurationWithInvalidConfigNotSaved() { + public void testSetWifiApConfigurationWithInvalidConfigNotSaved() throws Exception { when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); assertFalse(mWifiServiceImpl.setWifiApConfiguration(new WifiConfiguration(), TEST_PACKAGE_NAME)); @@ -1031,11 +970,9 @@ public class WifiServiceImplTest { /** * Ensure unpermitted callers are not able to retrieve the softap config. - * - * @throws SecurityException */ @Test - public void testGetWifiApConfigurationNotReturnedWithoutPermission() { + public void testGetWifiApConfigurationNotReturnedWithoutPermission() throws Exception { when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false); try { mWifiServiceImpl.getWifiApConfiguration(); @@ -1048,19 +985,17 @@ public class WifiServiceImplTest { * Ensure permitted callers are able to retrieve the softap config. */ @Test - public void testGetWifiApConfigurationSuccess() { - setupClientModeImplHandlerForRunWithScissors(); - + public void testGetWifiApConfigurationSuccess() throws Exception { mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel); - mWifiServiceImpl.setWifiHandlerLogForTest(mLog); - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); - when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); WifiConfiguration apConfig = new WifiConfiguration(); when(mWifiApConfigStore.getApConfiguration()).thenReturn(apConfig); + + mLooper.startAutoDispatch(); assertEquals(apConfig, mWifiServiceImpl.getWifiApConfiguration()); + mLooper.stopAutoDispatchAndIgnoreExceptions(); } /** @@ -1068,19 +1003,10 @@ public class WifiServiceImplTest { * broadcast. */ @Test - public void testGetWifiApEnabled() { - setupClientModeImplHandlerForRunWithScissors(); - - // set up WifiServiceImpl with a live thread for testing - HandlerThread serviceHandlerThread = createAndStartHandlerThreadForRunWithScissors(); - when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(serviceHandlerThread); - mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel); - mWifiServiceImpl.setWifiHandlerLogForTest(mLog); - + public void testGetWifiApEnabled() throws Exception { // ap should be disabled when wifi hasn't been started assertEquals(WifiManager.WIFI_AP_STATE_DISABLED, mWifiServiceImpl.getWifiApEnabledState()); - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); mLooper.dispatchAll(); @@ -1089,12 +1015,9 @@ public class WifiServiceImplTest { assertEquals(WifiManager.WIFI_AP_STATE_DISABLED, mWifiServiceImpl.getWifiApEnabledState()); // send an ap state change to verify WifiServiceImpl is updated - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); - - 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); + verifyApRegistration(); + mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_ENABLED, 0); + mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL); mLooper.dispatchAll(); assertEquals(WifiManager.WIFI_AP_STATE_FAILED, mWifiServiceImpl.getWifiApEnabledState()); @@ -1117,34 +1040,21 @@ public class WifiServiceImplTest { } /** - * 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); + public void testWifiControllerStartsWhenDeviceBootsWithWifiDisabled() { when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); - verify(mWifiController).start(); - verify(mWifiController, never()).sendMessage(CMD_WIFI_TOGGLED); + verify(mActiveModeWarden).start(); + verify(mActiveModeWarden, never()).wifiToggled(); } /** * Make sure we do start WifiController (wifi enabled) if the device is already decrypted. */ @Test - public void testWifiFullyStartsWhenDeviceIsDecryptedAtBootWithWifiEnabled() { - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + public void testWifiFullyStartsWhenDeviceBootsWithWifiEnabled() { when(mSettingsStore.handleWifiToggled(true)).thenReturn(true); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); when(mClientModeImpl.syncGetWifiState()).thenReturn(WIFI_STATE_DISABLED); @@ -1152,8 +1062,7 @@ public class WifiServiceImplTest { when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); mWifiServiceImpl.checkAndStartWifi(); - verify(mWifiController).start(); - verify(mWifiController).sendMessage(CMD_WIFI_TOGGLED); + verify(mActiveModeWarden).start(); } /** @@ -1163,8 +1072,7 @@ public class WifiServiceImplTest { public void testStartSoftApWithPermissionsAndNullConfig() { boolean result = mWifiServiceImpl.startSoftAp(null); assertTrue(result); - verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(1), anyInt(), mSoftApModeConfigCaptor.capture()); + verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture()); assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); } @@ -1175,7 +1083,7 @@ public class WifiServiceImplTest { public void testStartSoftApWithPermissionsAndInvalidConfig() { boolean result = mWifiServiceImpl.startSoftAp(mApConfig); assertFalse(result); - verifyZeroInteractions(mWifiController); + verifyZeroInteractions(mActiveModeWarden); } /** @@ -1186,57 +1094,48 @@ public class WifiServiceImplTest { WifiConfiguration config = createValidSoftApConfiguration(); boolean result = mWifiServiceImpl.startSoftAp(config); assertTrue(result); - verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(1), anyInt(), mSoftApModeConfigCaptor.capture()); + verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture()); assertEquals(config, mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); } /** - * Verify does not start softap when wificontroller is not started. - */ - @Test - public void testStartSoftApWhenInCryptDebounce() { - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true); - - WifiConfiguration config = createValidSoftApConfiguration(); - boolean result = mWifiServiceImpl.startSoftAp(config); - assertFalse(result); - verifyZeroInteractions(mWifiController); - } - - /** * 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")); + when(mContext.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK)) + .thenReturn(PackageManager.PERMISSION_DENIED); + doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( + eq(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK), any()); mWifiServiceImpl.startSoftAp(null); } /** - * Verify caller with proper permission can call stopSoftAp. + * Verify that startSoftAP() succeeds if the caller does not have the NETWORK_STACK permission + * but does have the MAINLINE_NETWORK_STACK permission. */ @Test - public void testStopSoftApWithPermissions() { - boolean result = mWifiServiceImpl.stopSoftAp(); + public void testStartSoftApWithoutNetworkStackWithMainlineNetworkStackSucceeds() { + when(mContext.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK)) + .thenReturn(PackageManager.PERMISSION_DENIED); + WifiConfiguration config = createValidSoftApConfiguration(); + boolean result = mWifiServiceImpl.startSoftAp(config); assertTrue(result); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), - eq(WifiManager.IFACE_IP_MODE_TETHERED)); + verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture()); + assertEquals(config, mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); + verify(mContext).enforceCallingOrSelfPermission( + eq(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK), any()); } /** - * Verify does not stop softap when wificontroller is not started. + * Verify caller with proper permission can call stopSoftAp. */ @Test - public void testStopSoftApWhenInCryptDebounce() { - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true); - + public void testStopSoftApWithPermissions() { boolean result = mWifiServiceImpl.stopSoftAp(); - assertFalse(result); - verifyZeroInteractions(mWifiController); + assertTrue(result); + verify(mActiveModeWarden).stopSoftAp(WifiManager.IFACE_IP_MODE_TETHERED); } /** @@ -1245,9 +1144,10 @@ public class WifiServiceImplTest { */ @Test(expected = SecurityException.class) public void testStopSoftApWithoutPermissionThrowsException() throws Exception { - doThrow(new SecurityException()).when(mContext) - .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_STACK), - eq("WifiService")); + when(mContext.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK)) + .thenReturn(PackageManager.PERMISSION_DENIED); + doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( + eq(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK), any()); mWifiServiceImpl.stopSoftAp(); } @@ -1256,10 +1156,11 @@ public class WifiServiceImplTest { */ @Test public void testStartScanFailureAppOpsIgnored() { - setupClientModeImplHandlerForRunWithScissors(); doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), SCAN_PACKAGE_NAME); + mLooper.startAutoDispatch(); assertFalse(mWifiServiceImpl.startScan(SCAN_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME); } @@ -1268,10 +1169,11 @@ public class WifiServiceImplTest { */ @Test public void testStartScanFailureInCanAccessScanResultsPermission() { - setupClientModeImplHandlerForRunWithScissors(); doThrow(new SecurityException()).when(mWifiPermissionsUtil) .enforceCanAccessScanResults(SCAN_PACKAGE_NAME, Process.myUid()); + mLooper.startAutoDispatch(); assertFalse(mWifiServiceImpl.startScan(SCAN_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME); } @@ -1280,11 +1182,12 @@ public class WifiServiceImplTest { */ @Test public void testStartScanFailureInRunWithScissors() { - setupClientModeImplHandlerForRunWithScissors(); - doReturn(false).when(mHandlerSpyForCmiRunWithScissors) - .runWithScissors(any(), anyLong()); + mWifiServiceImpl = makeWifiServiceImplWithMockRunnerWhichTimesOut(); + + mLooper.startAutoDispatch(); assertFalse(mWifiServiceImpl.startScan(SCAN_PACKAGE_NAME)); - verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verify(mScanRequestProxy, never()).startScan(anyInt(), eq(SCAN_PACKAGE_NAME)); } /** @@ -1292,10 +1195,12 @@ public class WifiServiceImplTest { */ @Test public void testStartScanFailureFromScanRequestProxy() { - setupClientModeImplHandlerForRunWithScissors(); when(mScanRequestProxy.startScan(anyInt(), anyString())).thenReturn(false); + + mLooper.startAutoDispatch(); assertFalse(mWifiServiceImpl.startScan(SCAN_PACKAGE_NAME)); - verify(mScanRequestProxy).startScan(Process.myUid(), SCAN_PACKAGE_NAME); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verify(mScanRequestProxy).startScan(Binder.getCallingUid(), SCAN_PACKAGE_NAME); } static final String TEST_SSID = "Sid's Place"; @@ -1369,7 +1274,7 @@ public class WifiServiceImplTest { */ @Test public void testConfiguredNetworkListAreEmptyFromAppWithoutPermission() throws Exception { - when(mClientModeImpl.syncGetConfiguredNetworks(anyInt(), any(), anyInt())) + when(mWifiConfigManager.getSavedNetworks(anyInt())) .thenReturn(TEST_WIFI_CONFIGURATION_LIST); // no permission = target SDK=Q && not a carrier app @@ -1388,7 +1293,7 @@ public class WifiServiceImplTest { */ @Test public void testConfiguredNetworkListAreEmptyOnSecurityException() throws Exception { - when(mClientModeImpl.syncGetConfiguredNetworks(anyInt(), any(), anyInt())) + when(mWifiConfigManager.getSavedNetworks(anyInt())) .thenReturn(TEST_WIFI_CONFIGURATION_LIST); doThrow(new SecurityException()).when(mWifiPermissionsUtil).enforceCanAccessScanResults( @@ -1407,7 +1312,7 @@ public class WifiServiceImplTest { */ @Test public void testConfiguredNetworkListAreVisibleFromPermittedApp() throws Exception { - when(mClientModeImpl.syncGetConfiguredNetworks(anyInt(), any(), anyInt())) + when(mWifiConfigManager.getSavedNetworks(anyInt())) .thenReturn(TEST_WIFI_CONFIGURATION_LIST); when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), @@ -1415,10 +1320,12 @@ public class WifiServiceImplTest { mWifiServiceImpl.mClientModeImplChannel = mAsyncChannel; + mLooper.startAutoDispatch(); ParceledListSlice<WifiConfiguration> configs = mWifiServiceImpl.getConfiguredNetworks(TEST_PACKAGE); + mLooper.stopAutoDispatchAndIgnoreExceptions(); - verify(mClientModeImpl).syncGetConfiguredNetworks(anyInt(), any(), eq(Process.WIFI_UID)); + verify(mWifiConfigManager).getSavedNetworks(eq(Process.WIFI_UID)); WifiConfigurationTestUtil.assertConfigurationsEqualForBackup( TEST_WIFI_CONFIGURATION_LIST, configs.getList()); } @@ -1429,18 +1336,19 @@ public class WifiServiceImplTest { * appropriate permissions. */ @Test - public void testPrivilegedConfiguredNetworkListAreEmptyFromAppWithoutPermission() - throws Exception { - when(mClientModeImpl.syncGetPrivilegedConfiguredNetwork(any())) + public void testPrivilegedConfiguredNetworkListAreEmptyFromAppWithoutPermission() { + when(mWifiConfigManager.getConfiguredNetworksWithPasswords()) .thenReturn(TEST_WIFI_CONFIGURATION_LIST); doThrow(new SecurityException()).when(mWifiPermissionsUtil).enforceCanAccessScanResults( anyString(), anyInt()); + mLooper.startAutoDispatch(); ParceledListSlice<WifiConfiguration> configs = mWifiServiceImpl.getPrivilegedConfiguredNetworks(TEST_PACKAGE); + mLooper.stopAutoDispatchAndIgnoreExceptions(); - assertEquals(null, configs); + assertNull(configs); } /** @@ -1448,18 +1356,19 @@ public class WifiServiceImplTest { * appropriate permissions, when enforceCanAccessScanResults raises a SecurityException. */ @Test - public void testPrivilegedConfiguredNetworkListAreEmptyOnSecurityException() throws Exception { - when(mClientModeImpl.syncGetPrivilegedConfiguredNetwork(any())) + public void testPrivilegedConfiguredNetworkListAreEmptyOnSecurityException() { + when(mWifiConfigManager.getConfiguredNetworksWithPasswords()) .thenReturn(TEST_WIFI_CONFIGURATION_LIST); doThrow(new SecurityException()).when(mWifiPermissionsUtil).enforceCanAccessScanResults( anyString(), anyInt()); + mLooper.startAutoDispatch(); ParceledListSlice<WifiConfiguration> configs = mWifiServiceImpl.getPrivilegedConfiguredNetworks(TEST_PACKAGE); + mLooper.stopAutoDispatchAndIgnoreExceptions(); - assertEquals(null, configs); - + assertNull(configs); } /** @@ -1467,14 +1376,14 @@ public class WifiServiceImplTest { * appropriate permissions (simulated by not throwing an exception for READ_WIFI_CREDENTIAL). */ @Test - public void testPrivilegedConfiguredNetworkListAreVisibleFromPermittedApp() throws Exception { - when(mClientModeImpl.syncGetPrivilegedConfiguredNetwork(any())) + public void testPrivilegedConfiguredNetworkListAreVisibleFromPermittedApp() { + when(mWifiConfigManager.getConfiguredNetworksWithPasswords()) .thenReturn(TEST_WIFI_CONFIGURATION_LIST); - mWifiServiceImpl.mClientModeImplChannel = mAsyncChannel; - + mLooper.startAutoDispatch(); ParceledListSlice<WifiConfiguration> configs = mWifiServiceImpl.getPrivilegedConfiguredNetworks(TEST_PACKAGE); + mLooper.stopAutoDispatchAndIgnoreExceptions(); WifiConfigurationTestUtil.assertConfigurationsEqualForBackup( TEST_WIFI_CONFIGURATION_LIST, configs.getList()); @@ -1485,8 +1394,6 @@ public class WifiServiceImplTest { */ @Test public void testGetScanResults() { - setupClientModeImplHandlerForRunWithScissors(); - ScanResult[] scanResults = ScanTestUtil.createScanDatas(new int[][]{{2417, 2427, 5180, 5170}})[0] .getResults(); @@ -1495,7 +1402,9 @@ public class WifiServiceImplTest { when(mScanRequestProxy.getScanResults()).thenReturn(scanResultList); String packageName = "test.com"; + mLooper.startAutoDispatch(); List<ScanResult> retrievedScanResultList = mWifiServiceImpl.getScanResults(packageName); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verify(mScanRequestProxy).getScanResults(); ScanTestUtil.assertScanResultsEquals(scanResults, @@ -1507,9 +1416,7 @@ public class WifiServiceImplTest { */ @Test public void testGetScanResultsFailureInRunWithScissors() { - setupClientModeImplHandlerForRunWithScissors(); - doReturn(false).when(mHandlerSpyForCmiRunWithScissors) - .runWithScissors(any(), anyLong()); + mWifiServiceImpl = makeWifiServiceImplWithMockRunnerWhichTimesOut(); ScanResult[] scanResults = ScanTestUtil.createScanDatas(new int[][]{{2417, 2427, 5180, 5170}})[0] @@ -1519,7 +1426,9 @@ public class WifiServiceImplTest { when(mScanRequestProxy.getScanResults()).thenReturn(scanResultList); String packageName = "test.com"; + mLooper.startAutoDispatch(); List<ScanResult> retrievedScanResultList = mWifiServiceImpl.getScanResults(packageName); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verify(mScanRequestProxy, never()).getScanResults(); assertTrue(retrievedScanResultList.isEmpty()); @@ -1528,11 +1437,10 @@ public class WifiServiceImplTest { private void registerLOHSRequestFull() { // allow test to proceed without a permission check failure when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) .thenReturn(false); - int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, - TEST_PACKAGE_NAME); + int result = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result); verifyCheckChangePermission(TEST_PACKAGE_NAME); } @@ -1555,7 +1463,7 @@ public class WifiServiceImplTest { doThrow(new SecurityException()).when(mContext) .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE), eq("WifiService")); - mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); + mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); } /** @@ -1567,7 +1475,7 @@ public class WifiServiceImplTest { doThrow(new SecurityException()) .when(mWifiPermissionsUtil).enforceLocationPermission(eq(TEST_PACKAGE_NAME), anyInt()); - mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); + mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); } /** @@ -1577,7 +1485,7 @@ public class WifiServiceImplTest { @Test(expected = SecurityException.class) public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationEnabled() { when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); - mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); + mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); } /** @@ -1587,44 +1495,30 @@ public class WifiServiceImplTest { public void testStartLocalOnlyHotspotFailsIfRequestorNotForegroundApp() throws Exception { when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(false); - int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, - TEST_PACKAGE_NAME); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(false); + int result = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result); } /** - * Only start LocalOnlyHotspot if device is in crypt debounce mode. - */ - @Test - public void testStartLocalOnlyHotspotFailsIfInCryptDebounce() throws Exception { - when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true); - int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, - TEST_PACKAGE_NAME); - assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result); - } - - /** - * Only start LocalOnlyHotspot if we are not tethering. + * Only start tethering if we are not tethering. */ @Test public void testTetheringDoesNotStartWhenAlreadyTetheringActive() throws Exception { - setupClientModeImplHandlerForPost(); - WifiConfiguration config = createValidSoftApConfiguration(); assertTrue(mWifiServiceImpl.startSoftAp(config)); - verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); + verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture()); assertEquals(config, mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); - + mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_ENABLED, 0); mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); + assertEquals(WIFI_AP_STATE_ENABLED, mWifiServiceImpl.getWifiApEnabledState()); + reset(mActiveModeWarden); // Start another session without a stop, that should fail. assertFalse(mWifiServiceImpl.startSoftAp(createValidSoftApConfiguration())); - verifyNoMoreInteractions(mWifiController); + + verifyNoMoreInteractions(mActiveModeWarden); } /** @@ -1632,14 +1526,15 @@ public class WifiServiceImplTest { */ @Test public void testHotspotDoesNotStartWhenAlreadyTethering() throws Exception { - setupClientModeImplHandlerForPost(); + WifiConfiguration config = createValidSoftApConfiguration(); + assertTrue(mWifiServiceImpl.startSoftAp(config)); + mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_ENABLED, 0); + mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); - mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); mLooper.dispatchAll(); - int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( - mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); + int returnCode = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); assertEquals(ERROR_INCOMPATIBLE_MODE, returnCode); } @@ -1649,11 +1544,10 @@ public class WifiServiceImplTest { @Test public void testHotspotDoesNotStartWhenTetheringDisallowed() throws Exception { when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); - when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) .thenReturn(true); - int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( - mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); + int returnCode = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); assertEquals(ERROR_TETHERING_DISALLOWED, returnCode); } @@ -1665,7 +1559,7 @@ public class WifiServiceImplTest { registerLOHSRequestFull(); // now do the second request that will fail - mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); + mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME); } /** @@ -1673,11 +1567,12 @@ public class WifiServiceImplTest { * registered callers. */ @Test - public void testStopLocalOnlyHotspotDoesNothingWithoutRegisteredRequests() { + public void testStopLocalOnlyHotspotDoesNothingWithoutRegisteredRequests() throws Exception { // allow test to proceed without a permission check failure mWifiServiceImpl.stopLocalOnlyHotspot(); + mLooper.dispatchAll(); // there is nothing registered, so this shouldn't do anything - verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt()); + verify(mActiveModeWarden, never()).stopSoftAp(anyInt()); } /** @@ -1685,16 +1580,17 @@ public class WifiServiceImplTest { * but there is still an active request */ @Test - public void testStopLocalOnlyHotspotDoesNothingWithARemainingRegisteredRequest() { + public void testStopLocalOnlyHotspotDoesNothingWithRemainingRequest() throws Exception { // register a request that will remain after the stopLOHS call mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo); - registerLOHSRequestFull(); + setupLocalOnlyHotspot(); // Since we are calling with the same pid, the second register call will be removed mWifiServiceImpl.stopLocalOnlyHotspot(); + mLooper.dispatchAll(); // there is still a valid registered request - do not tear down LOHS - verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt()); + verify(mActiveModeWarden, never()).stopSoftAp(anyInt()); } /** @@ -1702,19 +1598,20 @@ public class WifiServiceImplTest { * 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), anyInt(), any(SoftApModeConfiguration.class)); + public void testStopLocalOnlyHotspotTriggersStopWithOneRegisteredRequest() throws Exception { + setupLocalOnlyHotspot(); + + verify(mActiveModeWarden).startSoftAp(any()); + + mWifiServiceImpl.stopLocalOnlyHotspot(); + mLooper.dispatchAll(); // No permission check required for change_wifi_state. verify(mContext, never()).enforceCallingOrSelfPermission( eq("android.Manifest.permission.CHANGE_WIFI_STATE"), anyString()); - mWifiServiceImpl.stopLocalOnlyHotspot(); - // there is was only one request registered, we should tear down softap - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), - eq(WifiManager.IFACE_IP_MODE_LOCAL_ONLY)); + // there is was only one request registered, we should tear down LOHS + verify(mActiveModeWarden).stopSoftAp(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } /** @@ -1748,8 +1645,7 @@ public class WifiServiceImplTest { } private void verifyLohsBand(int expectedBand) { - verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(1), anyInt(), mSoftApModeConfigCaptor.capture()); + verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture()); final WifiConfiguration configuration = mSoftApModeConfigCaptor.getValue().mConfig; assertNotNull(configuration); assertEquals(expectedBand, configuration.apBand); @@ -1765,8 +1661,7 @@ public class WifiServiceImplTest { mWifiServiceImpl.new LocalOnlyRequestorCallback(); binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo); - verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(0), - eq(WifiManager.IFACE_IP_MODE_LOCAL_ONLY)); + verify(mActiveModeWarden, never()).stopSoftAp(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } /** @@ -1776,7 +1671,7 @@ public class WifiServiceImplTest { * cleared the requestor that died). */ @Test - public void testServiceImplNotCalledWhenBinderDeathTriggeredWithRegisteredRequests() { + public void testServiceImplNotCalledWhenBinderDeathTriggeredWithRequests() throws Exception { LocalOnlyRequestorCallback binderDeathCallback = mWifiServiceImpl.new LocalOnlyRequestorCallback(); @@ -1784,18 +1679,18 @@ public class WifiServiceImplTest { // softap mode mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo); - registerLOHSRequestFull(); + setupLocalOnlyHotspot(); binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo); - verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt()); + verify(mActiveModeWarden, never()).stopSoftAp(anyInt()); - reset(mWifiController); + reset(mActiveModeWarden); // 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(WifiManager.IFACE_IP_MODE_LOCAL_ONLY)); + mLooper.dispatchAll(); + verify(mActiveModeWarden).stopSoftAp(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } /** @@ -1853,14 +1748,12 @@ public class WifiServiceImplTest { */ @Test public void registerSoftApCallbackFailureOnLinkToDeath() throws Exception { - setupClientModeImplHandlerForPost(); - doThrow(new RemoteException()) .when(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); mWifiServiceImpl.registerSoftApCallback(mAppBinder, mClientSoftApCallback, 1); mLooper.dispatchAll(); verify(mClientSoftApCallback, never()).onStateChanged(WIFI_AP_STATE_DISABLED, 0); - verify(mClientSoftApCallback, never()).onNumClientsChanged(0); + verify(mClientSoftApCallback, never()).onConnectedClientsChanged(any()); } @@ -1882,7 +1775,7 @@ public class WifiServiceImplTest { mWifiServiceImpl.registerSoftApCallback(binder, callback, callbackIdentifier); mLooper.dispatchAll(); verify(callback).onStateChanged(WIFI_AP_STATE_DISABLED, 0); - verify(callback).onNumClientsChanged(0); + verify(callback).onConnectedClientsChanged(Mockito.<WifiClient>anyList()); } /** @@ -1890,8 +1783,6 @@ public class WifiServiceImplTest { */ @Test public void replacesOldCallbackWithNewCallbackWhenRegisteringTwice() throws Exception { - setupClientModeImplHandlerForPost(); - final int callbackIdentifier = 1; registerSoftApCallbackAndVerify(mAppBinder, mClientSoftApCallback, callbackIdentifier); registerSoftApCallbackAndVerify( @@ -1902,12 +1793,14 @@ public class WifiServiceImplTest { verify(mAnotherAppBinder).linkToDeath(any(), anyInt()); verify(mAnotherAppBinder, never()).unlinkToDeath(any(), anyInt()); - final int testNumClients = 4; - mStateMachineSoftApCallback.onNumClientsChanged(testNumClients); + final WifiClient testClient = new WifiClient(TEST_FACTORY_MAC_ADDR); + final List<WifiClient> testClients = new ArrayList(); + testClients.add(testClient); + mStateMachineSoftApCallback.onConnectedClientsChanged(testClients); mLooper.dispatchAll(); // Verify only the second callback is being called - verify(mClientSoftApCallback, never()).onNumClientsChanged(testNumClients); - verify(mAnotherSoftApCallback).onNumClientsChanged(testNumClients); + verify(mClientSoftApCallback, never()).onConnectedClientsChanged(testClients); + verify(mAnotherSoftApCallback).onConnectedClientsChanged(testClients); } /** @@ -1915,18 +1808,18 @@ public class WifiServiceImplTest { */ @Test public void unregisterSoftApCallbackRemovesCallback() throws Exception { - setupClientModeImplHandlerForPost(); - final int callbackIdentifier = 1; registerSoftApCallbackAndVerify(mClientSoftApCallback, callbackIdentifier); mWifiServiceImpl.unregisterSoftApCallback(callbackIdentifier); mLooper.dispatchAll(); - final int testNumClients = 4; - mStateMachineSoftApCallback.onNumClientsChanged(testNumClients); + final WifiClient testClient = new WifiClient(TEST_FACTORY_MAC_ADDR); + final List<WifiClient> testClients = new ArrayList(); + testClients.add(testClient); + mStateMachineSoftApCallback.onConnectedClientsChanged(testClients); mLooper.dispatchAll(); - verify(mClientSoftApCallback, never()).onNumClientsChanged(testNumClients); + verify(mClientSoftApCallback, never()).onConnectedClientsChanged(testClients); } /** @@ -1935,8 +1828,6 @@ public class WifiServiceImplTest { @Test public void unregisterSoftApCallbackDoesNotRemoveCallbackIfCallbackIdentifierNotMatching() throws Exception { - setupClientModeImplHandlerForPost(); - final int callbackIdentifier = 1; registerSoftApCallbackAndVerify(mClientSoftApCallback, callbackIdentifier); @@ -1944,10 +1835,12 @@ public class WifiServiceImplTest { mWifiServiceImpl.unregisterSoftApCallback(differentCallbackIdentifier); mLooper.dispatchAll(); - final int testNumClients = 4; - mStateMachineSoftApCallback.onNumClientsChanged(testNumClients); + final WifiClient testClient = new WifiClient(TEST_FACTORY_MAC_ADDR); + final List<WifiClient> testClients = new ArrayList(); + testClients.add(testClient); + mStateMachineSoftApCallback.onConnectedClientsChanged(testClients); mLooper.dispatchAll(); - verify(mClientSoftApCallback).onNumClientsChanged(testNumClients); + verify(mClientSoftApCallback).onConnectedClientsChanged(testClients); } /** @@ -1955,23 +1848,21 @@ public class WifiServiceImplTest { */ @Test public void correctCallbackIsCalledAfterAddingTwoCallbacksAndRemovingOne() throws Exception { - setupClientModeImplHandlerForPost(); - final int callbackIdentifier = 1; mWifiServiceImpl.registerSoftApCallback(mAppBinder, mClientSoftApCallback, callbackIdentifier); // Change state from default before registering the second callback - final int testNumClients = 4; + final List<WifiClient> testClients = new ArrayList(); mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_ENABLED, 0); - mStateMachineSoftApCallback.onNumClientsChanged(testNumClients); + mStateMachineSoftApCallback.onConnectedClientsChanged(testClients); // Register another callback and verify the new state is returned in the immediate callback final int anotherUid = 2; mWifiServiceImpl.registerSoftApCallback(mAppBinder, mAnotherSoftApCallback, anotherUid); mLooper.dispatchAll(); verify(mAnotherSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLED, 0); - verify(mAnotherSoftApCallback).onNumClientsChanged(testNumClients); + verify(mAnotherSoftApCallback).onConnectedClientsChanged(testClients); // unregister the fisrt callback mWifiServiceImpl.unregisterSoftApCallback(callbackIdentifier); @@ -1992,8 +1883,6 @@ public class WifiServiceImplTest { */ @Test public void registersForBinderDeathOnRegisterSoftApCallback() throws Exception { - setupClientModeImplHandlerForPost(); - final int callbackIdentifier = 1; registerSoftApCallbackAndVerify(mClientSoftApCallback, callbackIdentifier); verify(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); @@ -2004,8 +1893,6 @@ public class WifiServiceImplTest { */ @Test public void unregistersSoftApCallbackOnBinderDied() throws Exception { - setupClientModeImplHandlerForPost(); - ArgumentCaptor<IBinder.DeathRecipient> drCaptor = ArgumentCaptor.forClass(IBinder.DeathRecipient.class); final int callbackIdentifier = 1; @@ -2017,26 +1904,28 @@ public class WifiServiceImplTest { verify(mAppBinder).unlinkToDeath(drCaptor.getValue(), 0); // Verify callback is removed from the list as well - final int testNumClients = 4; - mStateMachineSoftApCallback.onNumClientsChanged(testNumClients); + final WifiClient testClient = new WifiClient(TEST_FACTORY_MAC_ADDR); + final List<WifiClient> testClients = new ArrayList(); + testClients.add(testClient); + mStateMachineSoftApCallback.onConnectedClientsChanged(testClients); mLooper.dispatchAll(); - verify(mClientSoftApCallback, never()).onNumClientsChanged(testNumClients); + verify(mClientSoftApCallback, never()).onConnectedClientsChanged(testClients); } /** * Verify that soft AP callback is called on NumClientsChanged event */ @Test - public void callsRegisteredCallbacksOnNumClientsChangedEvent() throws Exception { - setupClientModeImplHandlerForPost(); - + public void callsRegisteredCallbacksOnConnectedClientsChangedEvent() throws Exception { final int callbackIdentifier = 1; registerSoftApCallbackAndVerify(mClientSoftApCallback, callbackIdentifier); - final int testNumClients = 4; - mStateMachineSoftApCallback.onNumClientsChanged(testNumClients); + final WifiClient testClient = new WifiClient(TEST_FACTORY_MAC_ADDR); + final List<WifiClient> testClients = new ArrayList(); + testClients.add(testClient); + mStateMachineSoftApCallback.onConnectedClientsChanged(testClients); mLooper.dispatchAll(); - verify(mClientSoftApCallback).onNumClientsChanged(testNumClients); + verify(mClientSoftApCallback).onConnectedClientsChanged(testClients); } /** @@ -2044,8 +1933,6 @@ public class WifiServiceImplTest { */ @Test public void callsRegisteredCallbacksOnSoftApStateChangedEvent() throws Exception { - setupClientModeImplHandlerForPost(); - final int callbackIdentifier = 1; registerSoftApCallbackAndVerify(mClientSoftApCallback, callbackIdentifier); @@ -2059,12 +1946,10 @@ public class WifiServiceImplTest { * Ap events, even when no callbacks are registered. */ @Test - public void updatesSoftApStateAndNumClientsOnSoftApEvents() throws Exception { - setupClientModeImplHandlerForPost(); - - final int testNumClients = 4; + public void updatesSoftApStateAndConnectedClientsOnSoftApEvents() throws Exception { + final List<WifiClient> testClients = new ArrayList(); mStateMachineSoftApCallback.onStateChanged(WIFI_AP_STATE_ENABLED, 0); - mStateMachineSoftApCallback.onNumClientsChanged(testNumClients); + mStateMachineSoftApCallback.onConnectedClientsChanged(testClients); // Register callback after num clients and soft AP are changed. final int callbackIdentifier = 1; @@ -2072,7 +1957,7 @@ public class WifiServiceImplTest { callbackIdentifier); mLooper.dispatchAll(); verify(mClientSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLED, 0); - verify(mClientSoftApCallback).onNumClientsChanged(testNumClients); + verify(mClientSoftApCallback).onConnectedClientsChanged(testClients); } private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> { @@ -2083,263 +1968,173 @@ public class WifiServiceImplTest { } /** - * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE - * broadcast is received. + * Verify that onFailed is called for registered LOHS callers on SAP_START_FAILURE_GENERAL. */ @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())); + verifyApRegistration(); 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); + changeLohsState(WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_FAILED, message.what); - assertEquals(ERROR_GENERIC, message.arg1); + + verify(mLohsCallback).onHotspotFailed(ERROR_GENERIC); } /** - * 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. + * Verify that onFailed is called for registered LOHS callers on SAP_START_FAILURE_NO_CHANNEL. */ @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())); + verifyApRegistration(); 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); + changeLohsState(WIFI_AP_STATE_FAILED, + WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_NO_CHANNEL); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_FAILED, message.what); - assertEquals(ERROR_NO_CHANNEL, message.arg1); + verify(mLohsCallback).onHotspotFailed(ERROR_NO_CHANNEL); } /** - * 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. + * Common setup for starting a LOHS. */ - @Test - public void testRegisteredCallbacksTriggeredOnSoftApDisabling() throws Exception { - setupClientModeImplHandlerForPost(); - - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + private void setupLocalOnlyHotspot() throws Exception { when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); + verifyApRegistration(); registerLOHSRequestFull(); - mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); + changeLohsState(WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR); + mWifiServiceImpl.updateInterfaceIpState(mLohsInterfaceName, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_STARTED, message.what); - reset(mHandler); + verify(mLohsCallback).onHotspotStarted(any()); + } + + /** + * Verify that onStopped is called for registered LOHS callers when a callback is + * received with WIFI_AP_STATE_DISABLING and LOHS was active. + */ + @Test + public void testRegisteredCallbacksTriggeredOnSoftApDisabling() throws Exception { + setupLocalOnlyHotspot(); - TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, - WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); + changeLohsState(WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_STOPPED, message.what); + verify(mLohsCallback).onHotspotStopped(); } /** - * 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. + * Verify that onStopped is called for registered LOHS callers when a callback is + * received with WIFI_AP_STATE_DISABLED and LOHS was enabled. */ @Test public void testRegisteredCallbacksTriggeredOnSoftApDisabled() throws Exception { - setupClientModeImplHandlerForPost(); - - 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); + setupLocalOnlyHotspot(); - TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, - WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); + changeLohsState(WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_STOPPED, message.what); + verify(mLohsCallback).onHotspotStopped(); } /** - * Verify that no callbacks are called for registered LOHS callers when a WIFI_AP_STATE_CHANGE - * broadcast is received and the softap started. + * Verify that no callbacks are called for registered LOHS callers when a callback 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())); + verifyApRegistration(); 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); + changeLohsState(WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR); mLooper.dispatchAll(); - verify(mHandler, never()).handleMessage(any(Message.class)); + verifyZeroInteractions(ignoreStubs(mLohsCallback)); } /** * 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 + * callbacks are received with WIFI_AP_STATE_DISABLING and * WIFI_AP_STATE_DISABLED when LOHS was enabled. */ @Test public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApDisabling() throws Exception { - setupClientModeImplHandlerForPost(); + setupLocalOnlyHotspot(); - 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); + changeLohsState(WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + changeLohsState(WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_STOPPED, message.what); + verify(mLohsCallback).onHotspotStopped(); } /** * 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. + * callbacks 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(); + setupLocalOnlyHotspot(); - 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); + changeLohsState(WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + changeLohsState(WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_FAILED, message.what); - assertEquals(ERROR_GENERIC, message.arg1); + verify(mLohsCallback).onHotspotFailed(ERROR_GENERIC); } /** * Verify that onFailed is called for all registered LOHS callers when - * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED. + * callbacks 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())); + verifyApRegistration(); // 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); + changeLohsState(WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + changeLohsState(WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); 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(mLohsCallback).onHotspotFailed(ERROR_GENERIC); } /** * 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 + * callbacks are received with WIFI_AP_STATE_DISABLED when LOHS was * active. */ @Test public void testAllRegisteredCallbacksTriggeredWhenSoftApStops() throws Exception { - setupClientModeImplHandlerForPost(); - - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); + verifyApRegistration(); mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); @@ -2348,50 +2143,36 @@ public class WifiServiceImplTest { 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(mLohsCallback).onHotspotStarted(any()); + + reset(mRequestInfo); + clearInvocations(mLohsCallback); + + changeLohsState(WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + changeLohsState(WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); verify(mRequestInfo).sendHotspotStoppedMessage(); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(HOTSPOT_STOPPED, message.what); + verify(mLohsCallback).onHotspotStopped(); } /** * 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 + * callbacks are received with WIFI_AP_STATE_DISABLED when LOHS was * not active. */ @Test public void testAllRegisteredCallbacksTriggeredWhenSoftApStopsLOHSNotActive() throws Exception { - setupClientModeImplHandlerForPost(); - - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); + verifyApRegistration(); 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); + changeLohsState(WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + changeLohsState(WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); verify(mRequestInfo2).sendHotspotFailedMessage(ERROR_GENERIC); @@ -2405,13 +2186,10 @@ public class WifiServiceImplTest { */ @Test public void testLOHSReadyWithoutRegisteredRequestsStopsSoftApMode() { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), - eq(WifiManager.IFACE_IP_MODE_TETHERED)); + verify(mActiveModeWarden).stopSoftAp(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } /** @@ -2421,8 +2199,6 @@ public class WifiServiceImplTest { @Test public void testRegisteredLocalOnlyHotspotRequestorsGetOnStartedCallbackWhenReady() throws Exception { - setupClientModeImplHandlerForPost(); - registerLOHSRequestFull(); mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); @@ -2432,10 +2208,7 @@ public class WifiServiceImplTest { 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(mLohsCallback).onHotspotStarted(notNull()); } /** @@ -2445,10 +2218,9 @@ public class WifiServiceImplTest { @Test public void testRegisterLocalOnlyHotspotRequestAfterAlreadyStartedGetsOnStartedCallback() throws Exception { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + changeLohsState(WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR); mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); @@ -2456,11 +2228,7 @@ public class WifiServiceImplTest { 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(mLohsCallback).onHotspotStarted(any()); } /** @@ -2470,29 +2238,22 @@ public class WifiServiceImplTest { */ @Test public void testCallOnFailedLocalOnlyHotspotRequestWhenIpConfigFails() throws Exception { - setupClientModeImplHandlerForPost(); - - registerLOHSRequestFull(); + setupLocalOnlyHotspot(); + reset(mActiveModeWarden); 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); + verify(mLohsCallback).onHotspotFailed(ERROR_GENERIC); + verify(mActiveModeWarden).stopSoftAp(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(0), - eq(WifiManager.IFACE_IP_MODE_LOCAL_ONLY)); - - // sendMessage should only happen once since the requestor should be unregistered - reset(mHandler); + clearInvocations(mLohsCallback); // 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)); + verifyZeroInteractions(ignoreStubs(mLohsCallback)); } /** @@ -2501,13 +2262,11 @@ public class WifiServiceImplTest { */ @Test public void testStopSoftApWhenIpConfigFails() throws Exception { - setupClientModeImplHandlerForPost(); - + mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_CONFIGURATION_ERROR); mLooper.dispatchAll(); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), - eq(WifiManager.IFACE_IP_MODE_TETHERED)); + verify(mActiveModeWarden).stopSoftAp(IFACE_IP_MODE_TETHERED); } /** @@ -2516,24 +2275,24 @@ public class WifiServiceImplTest { */ @Test public void testCallOnFailedLocalOnlyHotspotRequestWhenTetheringStarts() throws Exception { - setupClientModeImplHandlerForPost(); - registerLOHSRequestFull(); + mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); + mLooper.dispatchAll(); + verify(mLohsCallback).onHotspotStarted(any()); + clearInvocations(mLohsCallback); + 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); + verify(mLohsCallback).onHotspotFailed(ERROR_INCOMPATIBLE_MODE); // sendMessage should only happen once since the requestor should be unregistered - reset(mHandler); + clearInvocations(mLohsCallback); mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); - verify(mHandler, never()).handleMessage(any(Message.class)); + verifyZeroInteractions(ignoreStubs(mLohsCallback)); } /** @@ -2546,7 +2305,7 @@ public class WifiServiceImplTest { registerLOHSRequestFull(); mLooper.dispatchAll(); - verify(mHandler, never()).handleMessage(any(Message.class)); + verifyZeroInteractions(ignoreStubs(mLohsCallback)); } /** @@ -2556,50 +2315,40 @@ public class WifiServiceImplTest { @Test public void testRegisterLocalOnlyHotspotRequestAfterStoppedNoOnStartedCallback() throws Exception { - setupClientModeImplHandlerForPost(); - - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); + verifyApRegistration(); // register a request so we don't drop the LOHS interface ip update mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); - + changeLohsState(WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR); 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); + verify(mLohsCallback).onHotspotStarted(any()); - reset(mHandler); + clearInvocations(mLohsCallback); // 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); + changeLohsState(WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + changeLohsState(WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - assertEquals(HOTSPOT_STOPPED, mMessageCaptor.getValue().what); + verify(mLohsCallback).onHotspotStopped(); - reset(mHandler); + clearInvocations(mLohsCallback); // now register a new caller - they should not get the onStarted callback - Messenger messenger2 = new Messenger(mHandler); - IBinder binder2 = mock(IBinder.class); + ILocalOnlyHotspotCallback callback2 = mock(ILocalOnlyHotspotCallback.class); + when(callback2.asBinder()).thenReturn(mock(IBinder.class)); - int result = mWifiServiceImpl.startLocalOnlyHotspot(messenger2, binder2, TEST_PACKAGE_NAME); + int result = mWifiServiceImpl.startLocalOnlyHotspot(callback2, TEST_PACKAGE_NAME); assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result); mLooper.dispatchAll(); - verify(mHandler, never()).handleMessage(any(Message.class)); + verify(mLohsCallback, never()).onHotspotStarted(any()); } /** @@ -2615,7 +2364,7 @@ public class WifiServiceImplTest { doThrow(new SecurityException()).when(mContext) .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), eq("WifiService")); - mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder); + mWifiServiceImpl.startWatchLocalOnlyHotspot(mLohsCallback); } /** @@ -2624,7 +2373,7 @@ public class WifiServiceImplTest { */ @Test(expected = UnsupportedOperationException.class) public void testStartWatchLocalOnlyHotspotNotSupported() { - mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder); + mWifiServiceImpl.startWatchLocalOnlyHotspot(mLohsCallback); } /** @@ -2658,27 +2407,31 @@ public class WifiServiceImplTest { config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); PackageManager pm = mock(PackageManager.class); - when(pm.hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(true); when(mContext.getPackageManager()).thenReturn(pm); - when(pm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(mApplicationInfo); + when(pm.getApplicationInfoAsUser(any(), anyInt(), any())).thenReturn(mApplicationInfo); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true); - when(mClientModeImpl.syncAddOrUpdatePasspointConfig(any(), - any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME))).thenReturn( - true); + when(mPasspointManager.addOrUpdateProvider( + any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME), eq(false))) + .thenReturn(true); + mLooper.startAutoDispatch(); assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncAddOrUpdatePasspointConfig(any(), - any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME)); - reset(mClientModeImpl); + verify(mPasspointManager).addOrUpdateProvider( + any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME), eq(false)); + reset(mPasspointManager); - when(mClientModeImpl.syncAddOrUpdatePasspointConfig(any(), - any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME))).thenReturn( - false); + when(mPasspointManager.addOrUpdateProvider( + any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME), anyBoolean())) + .thenReturn(false); + mLooper.startAutoDispatch(); assertEquals(-1, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); - verify(mClientModeImpl).syncAddOrUpdatePasspointConfig(any(), - any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verifyCheckChangePermission(TEST_PACKAGE_NAME); + verify(mPasspointManager).addOrUpdateProvider( + any(PasspointConfiguration.class), anyInt(), eq(TEST_PACKAGE_NAME), anyBoolean()); } /** @@ -2738,21 +2491,6 @@ public class WifiServiceImplTest { } /** - * Verify that the call to startSubscriptionProvisioning is not directed to the Passpoint - * specific API startSubscriptionProvisioning when the feature is not supported. - */ - @Test(expected = UnsupportedOperationException.class) - public void testStartSubscriptionProvisioniningPasspointUnsupported() throws Exception { - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETUP_WIZARD), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); - when(mPackageManager.hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(false); - mWifiServiceImpl.startSubscriptionProvisioning(mOsuProvider, mProvisioningCallback); - } - - /** * Verify that the call to startSubscriptionProvisioning is not redirected to the Passpoint * specific API startSubscriptionProvisioning when the caller provides invalid arguments */ @@ -2797,9 +2535,10 @@ public class WifiServiceImplTest { when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); when(mWifiPermissionsUtil.checkNetworkSetupWizardPermission(anyInt())).thenReturn(false); + mLooper.startAutoDispatch(); mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE_NAME); - - verify(mClientModeImpl).syncGetPasspointConfigs(any(), eq(false)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verify(mPasspointManager).getProviderConfigs(Binder.getCallingUid(), false); } /** @@ -2810,9 +2549,40 @@ public class WifiServiceImplTest { public void testGetPasspointConfigurationsWithPrivilegedPermissions() { when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + mLooper.startAutoDispatch(); mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE_NAME); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verify(mPasspointManager).getProviderConfigs(Binder.getCallingUid(), true); + } - verify(mClientModeImpl).syncGetPasspointConfigs(any(), eq(true)); + /** + * Verify that GetPasspointConfigurations will redirect calls to {@link PasspointManager} + * and returning the result that's returned from {@link PasspointManager}. + */ + @Test + public void testGetPasspointConfigurations() throws Exception { + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + + // 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(anyInt(), anyBoolean())) + .thenReturn(expectedConfigs); + mLooper.startAutoDispatch(); + assertEquals(expectedConfigs, mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + reset(mPasspointManager); + + when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean())) + .thenReturn(new ArrayList<PasspointConfiguration>()); + mLooper.startAutoDispatch(); + assertTrue(mWifiServiceImpl.getPasspointConfigurations(TEST_PACKAGE).isEmpty()); + mLooper.stopAutoDispatchAndIgnoreExceptions(); } /** @@ -2825,8 +2595,10 @@ public class WifiServiceImplTest { when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(anyInt())).thenReturn( false); + mLooper.startAutoDispatch(); mWifiServiceImpl.removePasspointConfiguration(TEST_FQDN, TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncRemovePasspointConfig(any(), eq(false), eq(TEST_FQDN)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verify(mPasspointManager).removeProvider(Binder.getCallingUid(), false, TEST_FQDN); } /** @@ -2838,8 +2610,10 @@ public class WifiServiceImplTest { when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(anyInt())).thenReturn( true); + mLooper.startAutoDispatch(); mWifiServiceImpl.removePasspointConfiguration(TEST_FQDN, TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncRemovePasspointConfig(any(), eq(true), eq(TEST_FQDN)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verify(mPasspointManager).removeProvider(Binder.getCallingUid(), true, TEST_FQDN); } /** @@ -2915,143 +2689,70 @@ public class WifiServiceImplTest { } /** - * Helper to test handling of async messages by wifi service when the message comes from an - * app without {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission. - */ - private void verifyAsyncChannelMessageHandlingWithoutChangePermisson( - int requestMsgWhat, int expectedReplyMsgwhat) throws RemoteException { - WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected(); - - int uidWithoutPermission = 5; - when(mWifiPermissionsUtil.checkChangePermission(eq(uidWithoutPermission))) - .thenReturn(false); - - Message request = Message.obtain(); - request.what = requestMsgWhat; - request.sendingUid = uidWithoutPermission; - - mLooper.startAutoDispatch(); - Message reply = tester.sendMessageSynchronously(request); - mLooper.stopAutoDispatch(); - - verify(mClientModeImpl, never()).sendMessage(any(Message.class)); - assertEquals(expectedReplyMsgwhat, reply.what); - assertEquals(WifiManager.NOT_AUTHORIZED, reply.arg1); - } - - /** - * Helper to test handling of async messages by wifi service when the message comes from an - * app without one of the privileged permissions. - */ - private void verifyAsyncChannelMessageHandlingWithoutPrivilegedPermissons( - int requestMsgWhat, int expectedReplyMsgwhat) throws RemoteException { - WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected(); - - int uidWithoutPermission = 5; - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETUP_WIZARD), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_STACK), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); - - Message request = Message.obtain(); - request.what = requestMsgWhat; - request.sendingUid = uidWithoutPermission; - - mLooper.startAutoDispatch(); - Message reply = tester.sendMessageSynchronously(request); - mLooper.stopAutoDispatch(); - - verify(mClientModeImpl, never()).sendMessage(any(Message.class)); - assertEquals(expectedReplyMsgwhat, reply.what); - assertEquals(WifiManager.NOT_AUTHORIZED, reply.arg1); - } - - /** * Verify that the CONNECT_NETWORK message received from an app without - * one of the privileged permission is rejected with the correct error code. + * one of the privileged permission is rejected with a security exception. */ @Test public void testConnectNetworkWithoutPrivilegedPermission() throws Exception { - verifyAsyncChannelMessageHandlingWithoutPrivilegedPermissons( - WifiManager.CONNECT_NETWORK, WifiManager.CONNECT_NETWORK_FAILED); + try { + mWifiServiceImpl.connect(mock(WifiConfiguration.class), TEST_NETWORK_ID, + mock(Binder.class), + mock(IActionListener.class), 0); + fail(); + } catch (SecurityException e) { + verify(mClientModeImpl, never()).connect(any(WifiConfiguration.class), anyInt(), + any(Binder.class), any(IActionListener.class), anyInt(), anyInt()); + } } /** * Verify that the FORGET_NETWORK message received from an app without - * one of the privileged permission is rejected with the correct error code. + * one of the privileged permission is rejected with a security exception. */ @Test public void testForgetNetworkWithoutPrivilegedPermission() throws Exception { - verifyAsyncChannelMessageHandlingWithoutPrivilegedPermissons( - WifiManager.SAVE_NETWORK, WifiManager.SAVE_NETWORK_FAILED); + try { + mWifiServiceImpl.forget(TEST_NETWORK_ID, mock(Binder.class), + mock(IActionListener.class), 0); + fail(); + } catch (SecurityException e) { + verify(mClientModeImpl, never()).forget(anyInt(), any(Binder.class), + any(IActionListener.class), anyInt(), anyInt()); + } } /** - * Verify that the DISABLE_NETWORK message received from an app without - * one of the privileged permission is rejected with the correct error code. + * Verify that the SAVE_NETWORK message received from an app without + * one of the privileged permission is rejected with a security exception. */ @Test - public void testDisableNetworkWithoutPrivilegedPermission() throws Exception { - verifyAsyncChannelMessageHandlingWithoutPrivilegedPermissons( - WifiManager.DISABLE_NETWORK, WifiManager.DISABLE_NETWORK_FAILED); + public void testSaveNetworkWithoutPrivilegedPermission() throws Exception { + try { + mWifiServiceImpl.save(mock(WifiConfiguration.class), mock(Binder.class), + mock(IActionListener.class), 0); + fail(); + } catch (SecurityException e) { + verify(mClientModeImpl, never()).save(any(WifiConfiguration.class), + any(Binder.class), any(IActionListener.class), anyInt(), anyInt()); + } } /** - * Verify that the RSSI_PKTCNT_FETCH message received from an app without - * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct - * error code. + * Verify that the PKT_CNT_FETCH message received from an app without + * CHANGE_WIFI_STATE} permission is rejected with a security exception. */ @Test - public void testRssiPktcntFetchWithoutChangePermission() throws Exception { - verifyAsyncChannelMessageHandlingWithoutChangePermisson( - WifiManager.RSSI_PKTCNT_FETCH, WifiManager.RSSI_PKTCNT_FETCH_FAILED); - } - - /** - * Helper to test handling of async messages by wifi service when the message comes from an - * app with {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission. - */ - private void verifyAsyncChannelMessageHandlingWithChangePermisson( - int requestMsgWhat, Object requestMsgObj) throws RemoteException { - WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected(); - - when(mWifiPermissionsUtil.checkChangePermission(anyInt())).thenReturn(true); - - Message request = Message.obtain(); - request.what = requestMsgWhat; - request.obj = requestMsgObj; - - tester.sendMessage(request); - mLooper.dispatchAll(); - - ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(messageArgumentCaptor.capture()); - assertEquals(requestMsgWhat, messageArgumentCaptor.getValue().what); - } - - /** - * Helper to test handling of async messages by wifi service when the message comes from an - * app with one of the privileged permissions. - */ - private void verifyAsyncChannelMessageHandlingWithPrivilegedPermissions( - int requestMsgWhat, Object requestMsgObj) throws RemoteException { - WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected(); - - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), - anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); - - Message request = Message.obtain(); - request.what = requestMsgWhat; - request.obj = requestMsgObj; - - tester.sendMessage(request); - mLooper.dispatchAll(); - - ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); - verify(mClientModeImpl).sendMessage(messageArgumentCaptor.capture()); - assertEquals(requestMsgWhat, messageArgumentCaptor.getValue().what); + public void testTxPacketCountFetchWithoutChangePermission() throws Exception { + doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "WifiService"); + try { + mWifiServiceImpl.getTxPacketCount(TEST_PACKAGE_NAME, mock(Binder.class), + mock(ITxPacketCountListener.class), 0); + fail(); + } catch (SecurityException e) { + verify(mClientModeImpl, never()).getTxPacketCount(any(Binder.class), + any(ITxPacketCountListener.class), anyInt(), anyInt()); + } } /** @@ -3060,8 +2761,13 @@ public class WifiServiceImplTest { */ @Test public void testConnectNetworkWithPrivilegedPermission() throws Exception { - verifyAsyncChannelMessageHandlingWithPrivilegedPermissions( - WifiManager.CONNECT_NETWORK, new WifiConfiguration()); + when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); + mWifiServiceImpl.connect(mock(WifiConfiguration.class), TEST_NETWORK_ID, + mock(Binder.class), + mock(IActionListener.class), 0); + verify(mClientModeImpl).connect(any(WifiConfiguration.class), anyInt(), + any(Binder.class), any(IActionListener.class), anyInt(), anyInt()); } /** @@ -3070,18 +2776,26 @@ public class WifiServiceImplTest { */ @Test public void testSaveNetworkWithPrivilegedPermission() throws Exception { - verifyAsyncChannelMessageHandlingWithPrivilegedPermissions( - WifiManager.SAVE_NETWORK, new WifiConfiguration()); + when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); + mWifiServiceImpl.save(mock(WifiConfiguration.class), mock(Binder.class), + mock(IActionListener.class), 0); + verify(mClientModeImpl).save(any(WifiConfiguration.class), + any(Binder.class), any(IActionListener.class), anyInt(), anyInt()); } /** - * Verify that the DISABLE_NETWORK message received from an app with + * Verify that the FORGET_NETWORK message received from an app with * one of the privileged permission is forwarded to ClientModeImpl. */ @Test - public void testDisableNetworkWithPrivilegedPermission() throws Exception { - verifyAsyncChannelMessageHandlingWithPrivilegedPermissions( - WifiManager.DISABLE_NETWORK, new Object()); + public void testForgetNetworkWithPrivilegedPermission() throws Exception { + when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); + mWifiServiceImpl.forget(TEST_NETWORK_ID, mock(Binder.class), mock(IActionListener.class), + 0); + verify(mClientModeImpl).forget(anyInt(), any(Binder.class), + any(IActionListener.class), anyInt(), anyInt()); } /** @@ -3090,52 +2804,12 @@ public class WifiServiceImplTest { */ @Test public void testRssiPktcntFetchWithChangePermission() throws Exception { - verifyAsyncChannelMessageHandlingWithChangePermisson( - WifiManager.RSSI_PKTCNT_FETCH, new Object()); + mWifiServiceImpl.getTxPacketCount(TEST_PACKAGE_NAME, mock(Binder.class), + mock(ITxPacketCountListener.class), 0); + verify(mClientModeImpl).getTxPacketCount(any(Binder.class), + any(ITxPacketCountListener.class), anyInt(), anyInt()); } - /** - * Verify that setCountryCode() calls WifiCountryCode object on succeess. - */ - @Test - public void testSetCountryCode() throws Exception { - mWifiServiceImpl.setCountryCode(TEST_COUNTRY_CODE); - verify(mWifiCountryCode).setCountryCode(TEST_COUNTRY_CODE); - } - - /** - * Verify that setCountryCode() fails and doesn't call WifiCountryCode object - * if the caller doesn't have CONNECTIVITY_INTERNAL permission. - */ - @Test(expected = SecurityException.class) - public void testSetCountryCodeFailsWithoutConnectivityInternalPermission() throws Exception { - doThrow(new SecurityException()).when(mContext) - .enforceCallingOrSelfPermission( - eq(android.Manifest.permission.CONNECTIVITY_INTERNAL), - eq("ConnectivityService")); - mWifiServiceImpl.setCountryCode(TEST_COUNTRY_CODE); - verify(mWifiCountryCode, never()).setCountryCode(TEST_COUNTRY_CODE); - } - - private void setupClientModeImplHandlerForPost() { - when(mWifiInjector.getClientModeImplHandler()).thenReturn(mHandler); - } - - /** - * Set the wifi state machine mock to return a handler created on test thread. - */ - private void setupClientModeImplHandlerForRunWithScissors() { - HandlerThread handlerThread = createAndStartHandlerThreadForRunWithScissors(); - mHandlerSpyForCmiRunWithScissors = spy(handlerThread.getThreadHandler()); - when(mWifiInjector.getClientModeImplHandler()) - .thenReturn(mHandlerSpyForCmiRunWithScissors); - } - - private HandlerThread createAndStartHandlerThreadForRunWithScissors() { - HandlerThread handlerThread = new HandlerThread("ServiceHandlerThreadForTest"); - handlerThread.start(); - return handlerThread; - } /** * Tests the scenario when a scan request arrives while the device is idle. In this case @@ -3143,8 +2817,6 @@ public class WifiServiceImplTest { */ @Test public void testHandleDelayedScanAfterIdleMode() throws Exception { - setupClientModeImplHandlerForRunWithScissors(); - when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), @@ -3162,6 +2834,7 @@ public class WifiServiceImplTest { // Tell the wifi service that idle mode ended. when(mPowerManager.isDeviceIdleMode()).thenReturn(false); TestUtil.sendIdleModeChanged(mBroadcastReceiverCaptor.getValue(), mContext); + mLooper.dispatchAll(); // Must scan now. verify(mScanRequestProxy).startScan(Process.myUid(), TEST_PACKAGE_NAME); @@ -3172,14 +2845,15 @@ public class WifiServiceImplTest { // Send another scan request. The device is not idle anymore, so it must be executed // immediately. + mLooper.startAutoDispatch(); assertTrue(mWifiServiceImpl.startScan(SCAN_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verify(mScanRequestProxy).startScan(Process.myUid(), SCAN_PACKAGE_NAME); } /** * Verify that if the caller has NETWORK_SETTINGS permission, then it doesn't need * CHANGE_WIFI_STATE permission. - * @throws Exception */ @Test public void testDisconnectWithNetworkSettingsPerm() throws Exception { @@ -3196,7 +2870,6 @@ public class WifiServiceImplTest { /** * Verify that if the caller doesn't have NETWORK_SETTINGS permission, it could still * get access with the CHANGE_WIFI_STATE permission. - * @throws Exception */ @Test public void testDisconnectWithChangeWifiStatePerm() throws Exception { @@ -3208,7 +2881,6 @@ public class WifiServiceImplTest { /** * Verify that the operation fails if the caller has neither NETWORK_SETTINGS or * CHANGE_WIFI_STATE permissions. - * @throws Exception */ @Test public void testDisconnectRejected() throws Exception { @@ -3226,7 +2898,6 @@ public class WifiServiceImplTest { @Test public void testPackageRemovedBroadcastHandling() { - when(mWifiInjector.getClientModeImplHandler()).thenReturn(mHandler); mWifiServiceImpl.checkAndStartWifi(); verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), argThat((IntentFilter filter) -> @@ -3239,10 +2910,14 @@ public class WifiServiceImplTest { intent.putExtra(Intent.EXTRA_UID, uid); intent.setData(Uri.fromParts("package", packageName, "")); mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); + mLooper.dispatchAll(); - verify(mClientModeImpl).removeAppConfigs(packageName, uid); + ArgumentCaptor<ApplicationInfo> aiCaptor = ArgumentCaptor.forClass(ApplicationInfo.class); + verify(mWifiConfigManager).removeNetworksForApp(aiCaptor.capture()); + assertNotNull(aiCaptor.getValue()); + assertEquals(uid, aiCaptor.getValue().uid); + assertEquals(packageName, aiCaptor.getValue().packageName); - mLooper.dispatchAll(); verify(mScanRequestProxy).clearScanRequestTimestampsForApp(packageName, uid); verify(mWifiNetworkSuggestionsManager).removeApp(packageName); verify(mClientModeImpl).removeNetworkRequestUserApprovedAccessPointsForApp(packageName); @@ -3251,7 +2926,6 @@ public class WifiServiceImplTest { @Test public void testPackageRemovedBroadcastHandlingWithNoUid() { - when(mWifiInjector.getClientModeImplHandler()).thenReturn(mHandler); mWifiServiceImpl.checkAndStartWifi(); verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), argThat((IntentFilter filter) -> @@ -3263,7 +2937,7 @@ public class WifiServiceImplTest { intent.setData(Uri.fromParts("package", packageName, "")); mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); - verify(mClientModeImpl, never()).removeAppConfigs(anyString(), anyInt()); + verify(mWifiConfigManager, never()).removeNetworksForApp(any()); mLooper.dispatchAll(); verify(mScanRequestProxy, never()).clearScanRequestTimestampsForApp(anyString(), anyInt()); @@ -3275,7 +2949,6 @@ public class WifiServiceImplTest { @Test public void testPackageRemovedBroadcastHandlingWithNoPackageName() { - when(mWifiInjector.getClientModeImplHandler()).thenReturn(mHandler); mWifiServiceImpl.checkAndStartWifi(); verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), argThat((IntentFilter filter) -> @@ -3287,7 +2960,7 @@ public class WifiServiceImplTest { intent.putExtra(Intent.EXTRA_UID, uid); mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); - verify(mClientModeImpl, never()).removeAppConfigs(anyString(), anyInt()); + verify(mWifiConfigManager, never()).removeNetworksForApp(any()); mLooper.dispatchAll(); verify(mScanRequestProxy, never()).clearScanRequestTimestampsForApp(anyString(), anyInt()); @@ -3309,8 +2982,9 @@ public class WifiServiceImplTest { Intent intent = new Intent(Intent.ACTION_USER_REMOVED); intent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); + mLooper.dispatchAll(); - verify(mClientModeImpl).removeUserConfigs(userHandle); + verify(mWifiConfigManager).removeNetworksForUser(userHandle); } @Test @@ -3326,7 +3000,7 @@ public class WifiServiceImplTest { intent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); - verify(mClientModeImpl, never()).removeUserConfigs(userHandle); + verify(mWifiConfigManager, never()).removeNetworksForUser(anyInt()); } /** @@ -3527,8 +3201,6 @@ public class WifiServiceImplTest { */ @Test public void registerTrafficStateCallbackAndVerify() throws Exception { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.registerTrafficStateCallback( mAppBinder, mTrafficStateCallback, TEST_TRAFFIC_STATE_CALLBACK_IDENTIFIER); mLooper.dispatchAll(); @@ -3541,8 +3213,6 @@ public class WifiServiceImplTest { */ @Test public void unregisterTrafficStateCallbackAndVerify() throws Exception { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.unregisterTrafficStateCallback(0); mLooper.dispatchAll(); verify(mWifiTrafficPoller).removeCallback(0); @@ -3604,8 +3274,6 @@ public class WifiServiceImplTest { */ @Test public void registerNetworkRequestMatchCallbackAndVerify() throws Exception { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.registerNetworkRequestMatchCallback( mAppBinder, mNetworkRequestMatchCallback, TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER); @@ -3621,8 +3289,6 @@ public class WifiServiceImplTest { */ @Test public void unregisterNetworkRequestMatchCallbackAndVerify() throws Exception { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.unregisterNetworkRequestMatchCallback( TEST_NETWORK_REQUEST_MATCH_CALLBACK_IDENTIFIER); mLooper.dispatchAll(); @@ -3635,8 +3301,6 @@ public class WifiServiceImplTest { */ @Test public void testFactoryReset() throws Exception { - setupClientModeImplHandlerForPost(); - when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); @@ -3648,44 +3312,26 @@ public class WifiServiceImplTest { config.setHomeSp(homeSp); mWifiServiceImpl.mClientModeImplChannel = mAsyncChannel; - when(mClientModeImpl.syncGetConfiguredNetworks(anyInt(), any(), anyInt())) + when(mWifiConfigManager.getSavedNetworks(anyInt())) .thenReturn(Arrays.asList(network)); - when(mClientModeImpl.syncGetPasspointConfigs(any(), anyBoolean())) + when(mPasspointManager.getProviderConfigs(anyInt(), anyBoolean())) .thenReturn(Arrays.asList(config)); + mLooper.startAutoDispatch(); mWifiServiceImpl.factoryReset(TEST_PACKAGE_NAME); - mLooper.dispatchAll(); + mLooper.stopAutoDispatchAndIgnoreExceptions(); - verify(mClientModeImpl).syncRemoveNetwork(mAsyncChannel, network.networkId); - verify(mClientModeImpl).syncRemovePasspointConfig(mAsyncChannel, true, fqdn); - verify(mWifiConfigManager).clearDeletedEphemeralNetworks(); - verify(mClientModeImpl).clearNetworkRequestUserApprovedAccessPoints(); - verify(mWifiNetworkSuggestionsManager).clear(); - verify(mWifiScoreCard).clear(); - } - - /** - * Verify that Passpoint configuration is not removed in factoryReset if Passpoint feature - * is not supported. - */ - @Test - public void testFactoryResetWithoutPasspointSupport() throws Exception { - setupClientModeImplHandlerForPost(); - - mWifiServiceImpl.mClientModeImplChannel = mAsyncChannel; - when(mPackageManager.hasSystemFeature( - PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(false); - - mWifiServiceImpl.factoryReset(TEST_PACKAGE_NAME); + // Let the final post inside the |factoryReset| method run to completion. mLooper.dispatchAll(); - verify(mClientModeImpl).syncGetConfiguredNetworks(anyInt(), any(), anyInt()); - verify(mClientModeImpl, never()).syncGetPasspointConfigs(any(), anyBoolean()); - verify(mClientModeImpl, never()).syncRemovePasspointConfig( - any(), anyBoolean(), anyString()); + verify(mWifiConfigManager).removeNetwork( + network.networkId, Binder.getCallingUid(), TEST_PACKAGE_NAME); + verify(mPasspointManager).removeProvider(anyInt(), anyBoolean(), eq(fqdn)); verify(mWifiConfigManager).clearDeletedEphemeralNetworks(); verify(mClientModeImpl).clearNetworkRequestUserApprovedAccessPoints(); verify(mWifiNetworkSuggestionsManager).clear(); + verify(mWifiScoreCard).clear(); + verify(mPasspointManager).getProviderConfigs(anyInt(), anyBoolean()); } /** @@ -3697,32 +3343,32 @@ public class WifiServiceImplTest { doThrow(new SecurityException()).when(mContext) .enforceCallingOrSelfPermission(eq(Manifest.permission.CONNECTIVITY_INTERNAL), eq("ConnectivityService")); - mWifiServiceImpl.mClientModeImplChannel = mAsyncChannel; - try { mWifiServiceImpl.factoryReset(TEST_PACKAGE_NAME); fail(); } catch (SecurityException e) { } - verify(mClientModeImpl, never()).syncGetConfiguredNetworks(anyInt(), any(), anyInt()); - verify(mClientModeImpl, never()).syncGetPasspointConfigs(any(), eq(false)); + verify(mWifiConfigManager, never()).getSavedNetworks(anyInt()); + verify(mPasspointManager, never()).getProviderConfigs(anyInt(), anyBoolean()); } /** * Verify that add or update networks is not allowed for apps targeting Q SDK. */ @Test - public void testAddOrUpdateNetworkIsNotAllowedForAppsTargetingQSDK() throws Exception { - mLooper.dispatchAll(); + public void testAddOrUpdateNetworkIsNotAllowedForAppsTargetingQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); - when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0); + when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any())).thenReturn( + new NetworkUpdateResult(0)); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + mLooper.startAutoDispatch(); assertEquals(-1, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl, never()).syncAddOrUpdateNetwork(any(), any()); + verify(mWifiConfigManager, never()).addOrUpdateNetwork(any(), anyInt(), any()); verify(mWifiMetrics, never()).incrementNumAddOrUpdateNetworkCalls(); } @@ -3730,19 +3376,21 @@ public class WifiServiceImplTest { * Verify that add or update networks is allowed for apps targeting below Q SDK. */ @Test - public void testAddOrUpdateNetworkIsAllowedForAppsTargetingBelowQSDK() throws Exception { - mLooper.dispatchAll(); + public void testAddOrUpdateNetworkIsAllowedForAppsTargetingBelowQSdk() throws Exception { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); - when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0); + when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any())).thenReturn( + new NetworkUpdateResult(0)); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + mLooper.startAutoDispatch(); assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncAddOrUpdateNetwork(any(), any()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt(), any()); verify(mWifiMetrics).incrementNumAddOrUpdateNetworkCalls(); } @@ -3751,21 +3399,23 @@ public class WifiServiceImplTest { */ @Test public void testAddOrUpdateNetworkIsAllowedForSettingsApp() throws Exception { - mLooper.dispatchAll(); when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.P; - when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0); + when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any())).thenReturn( + new NetworkUpdateResult(0)); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + mLooper.startAutoDispatch(); assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); // Ensure that we don't check for change permission. verify(mContext, never()).enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_WIFI_STATE, "WifiService"); verify(mAppOpsManager, never()).noteOp( AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncAddOrUpdateNetwork(any(), any()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt(), any()); verify(mWifiMetrics).incrementNumAddOrUpdateNetworkCalls(); } @@ -3774,17 +3424,19 @@ public class WifiServiceImplTest { */ @Test public void testAddOrUpdateNetworkIsAllowedForSystemApp() throws Exception { - mLooper.dispatchAll(); doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; - when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0); + when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any())).thenReturn( + new NetworkUpdateResult(0)); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + mLooper.startAutoDispatch(); assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncAddOrUpdateNetwork(any(), any()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt(), any()); verify(mWifiMetrics).incrementNumAddOrUpdateNetworkCalls(); } @@ -3793,20 +3445,22 @@ public class WifiServiceImplTest { */ @Test public void testAddOrUpdateNetworkIsAllowedForAppsWithSystemAlertPermission() throws Exception { - mLooper.dispatchAll(); doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.checkSystemAlertWindowPermission( Process.myUid(), TEST_PACKAGE_NAME)).thenReturn(true); - when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0); + when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any())).thenReturn( + new NetworkUpdateResult(0)); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + mLooper.startAutoDispatch(); assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verifyCheckChangePermission(TEST_PACKAGE_NAME); verify(mWifiPermissionsUtil).checkSystemAlertWindowPermission(anyInt(), anyString()); - verify(mClientModeImpl).syncAddOrUpdateNetwork(any(), any()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt(), any()); verify(mWifiMetrics).incrementNumAddOrUpdateNetworkCalls(); } @@ -3815,19 +3469,20 @@ public class WifiServiceImplTest { */ @Test public void testAddOrUpdateNetworkIsAllowedForDOApp() throws Exception { - mLooper.dispatchAll(); doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); - when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy( - Process.myUid(), DeviceAdminInfo.USES_POLICY_DEVICE_OWNER)) + when(mWifiPermissionsUtil.isDeviceOwner(Binder.getCallingUid(), TEST_PACKAGE_NAME)) .thenReturn(true); - when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0); + when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any())).thenReturn( + new NetworkUpdateResult(0)); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + mLooper.startAutoDispatch(); assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncAddOrUpdateNetwork(any(), any()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt(), any()); verify(mWifiMetrics).incrementNumAddOrUpdateNetworkCalls(); } @@ -3836,19 +3491,20 @@ public class WifiServiceImplTest { */ @Test public void testAddOrUpdateNetworkIsAllowedForPOApp() throws Exception { - mLooper.dispatchAll(); doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); - when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy( - Process.myUid(), DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) + when(mWifiPermissionsUtil.isProfileOwner(Binder.getCallingUid(), TEST_PACKAGE_NAME)) .thenReturn(true); - when(mClientModeImpl.syncAddOrUpdateNetwork(any(), any())).thenReturn(0); + when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any())).thenReturn( + new NetworkUpdateResult(0)); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + mLooper.startAutoDispatch(); assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl).syncAddOrUpdateNetwork(any(), any()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt(), any()); verify(mWifiMetrics).incrementNumAddOrUpdateNetworkCalls(); } @@ -3856,35 +3512,79 @@ public class WifiServiceImplTest { * Verify that enableNetwork is allowed for privileged Apps */ @Test - public void testEnableNetworkAllowedForPrivilegedApps() throws Exception { - mLooper.dispatchAll(); + public void testEnableNetworkWithDisableOthersAllowedForPrivilegedApps() throws Exception { when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); - mWifiServiceImpl.enableNetwork(TEST_NETWORK_ID, true, TEST_PACKAGE_NAME); + doAnswer(new MockAnswerUtil.AnswerWithArguments() { + public void answer(WifiConfiguration config, int netId, IBinder binder, + IActionListener callback, int callbackIdentifier, int callingUid) { + try { + callback.onSuccess(); // return success + } catch (RemoteException e) { } + } + }).when(mClientModeImpl).connect( + isNull(), eq(TEST_NETWORK_ID), any(), any(), anyInt(), anyInt()); + + assertTrue(mWifiServiceImpl.enableNetwork(TEST_NETWORK_ID, true, TEST_PACKAGE_NAME)); - verify(mClientModeImpl).syncEnableNetwork(eq(mAsyncChannel), eq(TEST_NETWORK_ID), - eq(true)); + verify(mClientModeImpl).connect(isNull(), eq(TEST_NETWORK_ID), any(), any(), anyInt(), + anyInt()); verify(mWifiMetrics).incrementNumEnableNetworkCalls(); } /** - * Verify that enableNetwork is allowed for Apps targeting a SDK version less than Q + * Verify that enableNetwork (with disableOthers=true) is allowed for Apps targeting a SDK + * version less than Q */ @Test - public void testEnabledNetworkAllowedForAppsTargetingLessThanQ() throws Exception { + public void testEnabledNetworkWithDisableOthersAllowedForAppsTargetingBelowQSdk() + throws Exception { mLooper.dispatchAll(); doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true); - mWifiServiceImpl.enableNetwork(TEST_NETWORK_ID, true, TEST_PACKAGE_NAME); + doAnswer(new MockAnswerUtil.AnswerWithArguments() { + public void answer(WifiConfiguration config, int netId, IBinder binder, + IActionListener callback, int callbackIdentifier, int callingUid) { + try { + callback.onSuccess(); // return success + } catch (RemoteException e) { } + } + }).when(mClientModeImpl).connect( + isNull(), eq(TEST_NETWORK_ID), any(), any(), anyInt(), anyInt()); - verify(mClientModeImpl).syncEnableNetwork(eq(mAsyncChannel), eq(TEST_NETWORK_ID), - eq(true)); + assertTrue(mWifiServiceImpl.enableNetwork(TEST_NETWORK_ID, true, TEST_PACKAGE_NAME)); + + verify(mClientModeImpl).connect(isNull(), eq(TEST_NETWORK_ID), any(), any(), anyInt(), + anyInt()); + verify(mWifiMetrics).incrementNumEnableNetworkCalls(); + } + + /** + * Verify that enableNetwork (with disableOthers=false) is allowed for Apps targeting a SDK + * version less than Q + */ + @Test + public void testEnabledNetworkWithoutDisableOthersAllowedForAppsTargetingBelowQSdk() + throws Exception { + mLooper.dispatchAll(); + doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) + .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); + when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), + eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true); + + when(mWifiConfigManager.enableNetwork(anyInt(), anyBoolean(), anyInt(), anyString())) + .thenReturn(true); + mLooper.startAutoDispatch(); + assertTrue(mWifiServiceImpl.enableNetwork(TEST_NETWORK_ID, false, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + verify(mWifiConfigManager).enableNetwork(eq(TEST_NETWORK_ID), eq(false), + eq(Binder.getCallingUid()), eq(TEST_PACKAGE_NAME)); verify(mWifiMetrics).incrementNumEnableNetworkCalls(); } @@ -3893,13 +3593,13 @@ public class WifiServiceImplTest { */ @Test public void testEnableNetworkNotAllowedForAppsTargetingQ() throws Exception { - mLooper.dispatchAll(); doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); mWifiServiceImpl.enableNetwork(TEST_NETWORK_ID, true, TEST_PACKAGE_NAME); - verify(mClientModeImpl, never()).syncEnableNetwork(anyObject(), anyInt(), anyBoolean()); + verify(mClientModeImpl, never()).connect(isNull(), anyInt(), any(), any(), anyInt(), + anyInt()); verify(mWifiMetrics, never()).incrementNumEnableNetworkCalls(); } @@ -3909,24 +3609,38 @@ public class WifiServiceImplTest { */ @Test public void testAddNetworkSuggestions() { - setupClientModeImplHandlerForRunWithScissors(); - when(mWifiNetworkSuggestionsManager.add(any(), anyInt(), anyString())) .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS); + mLooper.startAutoDispatch(); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiServiceImpl.addNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); when(mWifiNetworkSuggestionsManager.add(any(), anyInt(), anyString())) .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE); + mLooper.startAutoDispatch(); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE, mWifiServiceImpl.addNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + + verify(mWifiNetworkSuggestionsManager, times(2)).add( + any(), eq(Binder.getCallingUid()), eq(TEST_PACKAGE_NAME)); + } + + /** + * Ensure that we don't invoke {@link WifiNetworkSuggestionsManager} to add network + * suggestions when the looper sync call times out. + */ + @Test + public void testAddNetworkSuggestionsFailureInRunWithScissors() { + mWifiServiceImpl = makeWifiServiceImplWithMockRunnerWhichTimesOut(); - doReturn(false).when(mHandlerSpyForCmiRunWithScissors) - .runWithScissors(any(), anyLong()); + mLooper.startAutoDispatch(); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL, mWifiServiceImpl.addNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); - verify(mWifiNetworkSuggestionsManager, times(2)).add( + verify(mWifiNetworkSuggestionsManager, never()).add( any(), eq(Binder.getCallingUid()), eq(TEST_PACKAGE_NAME)); } @@ -3936,28 +3650,72 @@ public class WifiServiceImplTest { */ @Test public void testRemoveNetworkSuggestions() { - setupClientModeImplHandlerForRunWithScissors(); - when(mWifiNetworkSuggestionsManager.remove(any(), anyInt(), anyString())) .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID); + mLooper.startAutoDispatch(); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID, mWifiServiceImpl.removeNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); when(mWifiNetworkSuggestionsManager.remove(any(), anyInt(), anyString())) .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS); + mLooper.startAutoDispatch(); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiServiceImpl.removeNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + + verify(mWifiNetworkSuggestionsManager, times(2)).remove(any(), anyInt(), + eq(TEST_PACKAGE_NAME)); + } - doReturn(false).when(mHandlerSpyForCmiRunWithScissors) - .runWithScissors(any(), anyLong()); + /** + * Ensure that we don't invoke {@link WifiNetworkSuggestionsManager} to remove network + * suggestions when the looper sync call times out. + */ + @Test + public void testRemoveNetworkSuggestionsFailureInRunWithScissors() { + mWifiServiceImpl = makeWifiServiceImplWithMockRunnerWhichTimesOut(); + + mLooper.startAutoDispatch(); assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL, mWifiServiceImpl.removeNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); - verify(mWifiNetworkSuggestionsManager, times(2)).remove(any(), anyInt(), + verify(mWifiNetworkSuggestionsManager, never()).remove(any(), anyInt(), eq(TEST_PACKAGE_NAME)); } /** + * Ensure that we invoke {@link WifiNetworkSuggestionsManager} to get network + * suggestions. + */ + @Test + public void testGetNetworkSuggestions() { + List<WifiNetworkSuggestion> testList = new ArrayList<>(); + when(mWifiNetworkSuggestionsManager.get(anyString())).thenReturn(testList); + mLooper.startAutoDispatch(); + assertEquals(testList, mWifiServiceImpl.getNetworkSuggestions(TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + + verify(mWifiNetworkSuggestionsManager).get(eq(TEST_PACKAGE_NAME)); + } + + /** + * Ensure that we don't invoke {@link WifiNetworkSuggestionsManager} to get network + * suggestions when the looper sync call times out. + */ + @Test + public void testGetNetworkSuggestionsFailureInRunWithScissors() { + mWifiServiceImpl = makeWifiServiceImplWithMockRunnerWhichTimesOut(); + + mLooper.startAutoDispatch(); + assertTrue(mWifiServiceImpl.getNetworkSuggestions(TEST_PACKAGE_NAME).isEmpty()); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + + verify(mWifiNetworkSuggestionsManager, never()).get(eq(TEST_PACKAGE_NAME)); + } + + /** * Verify that if the caller has NETWORK_SETTINGS permission, then it can invoke * {@link WifiManager#disableEphemeralNetwork(String)}. */ @@ -3966,7 +3724,8 @@ public class WifiServiceImplTest { when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); mWifiServiceImpl.disableEphemeralNetwork(new String(), TEST_PACKAGE_NAME); - verify(mClientModeImpl).disableEphemeralNetwork(anyString()); + mLooper.dispatchAll(); + verify(mWifiConfigManager).disableEphemeralNetwork(anyString()); } /** @@ -3978,7 +3737,8 @@ public class WifiServiceImplTest { when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); mWifiServiceImpl.disableEphemeralNetwork(new String(), TEST_PACKAGE_NAME); - verify(mClientModeImpl, never()).disableEphemeralNetwork(anyString()); + mLooper.dispatchAll(); + verify(mWifiConfigManager, never()).disableEphemeralNetwork(anyString()); } /** @@ -3986,10 +3746,11 @@ public class WifiServiceImplTest { */ @Test public void testGetFactoryMacAddresses() throws Exception { - setupClientModeImplHandlerForRunWithScissors(); when(mClientModeImpl.getFactoryMacAddress()).thenReturn(TEST_FACTORY_MAC); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + mLooper.startAutoDispatch(); final String[] factoryMacs = mWifiServiceImpl.getFactoryMacAddresses(); + mLooper.stopAutoDispatchAndIgnoreExceptions(); assertEquals(1, factoryMacs.length); assertEquals(TEST_FACTORY_MAC, factoryMacs[0]); verify(mClientModeImpl).getFactoryMacAddress(); @@ -4001,11 +3762,12 @@ public class WifiServiceImplTest { */ @Test public void testGetFactoryMacAddressesPostFail() throws Exception { - setupClientModeImplHandlerForRunWithScissors(); - doReturn(false).when(mHandlerSpyForCmiRunWithScissors) - .runWithScissors(any(), anyLong()); + mWifiServiceImpl = makeWifiServiceImplWithMockRunnerWhichTimesOut(); + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + mLooper.startAutoDispatch(); assertNull(mWifiServiceImpl.getFactoryMacAddresses()); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verify(mClientModeImpl, never()).getFactoryMacAddress(); } @@ -4014,10 +3776,11 @@ public class WifiServiceImplTest { */ @Test public void testGetFactoryMacAddressesFail() throws Exception { - setupClientModeImplHandlerForRunWithScissors(); when(mClientModeImpl.getFactoryMacAddress()).thenReturn(null); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + mLooper.startAutoDispatch(); assertNull(mWifiServiceImpl.getFactoryMacAddresses()); + mLooper.stopAutoDispatchAndIgnoreExceptions(); verify(mClientModeImpl).getFactoryMacAddress(); } @@ -4027,11 +3790,12 @@ public class WifiServiceImplTest { */ @Test public void testGetFactoryMacAddressesFailNoNetworkSettingsPermission() throws Exception { - setupClientModeImplHandlerForRunWithScissors(); when(mClientModeImpl.getFactoryMacAddress()).thenReturn(TEST_FACTORY_MAC); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); try { + mLooper.startAutoDispatch(); mWifiServiceImpl.getFactoryMacAddresses(); + mLooper.stopAutoDispatchAndIgnoreExceptions(); fail(); } catch (SecurityException e) { assertTrue("Exception message should contain 'factory MAC'", @@ -4061,8 +3825,6 @@ public class WifiServiceImplTest { */ @Test public void setDeviceMobilityStateRunsOnHandler() { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.setDeviceMobilityState(DEVICE_MOBILITY_STATE_STATIONARY); verify(mClientModeImpl, never()).setDeviceMobilityState(anyInt()); mLooper.dispatchAll(); @@ -4124,8 +3886,6 @@ public class WifiServiceImplTest { */ @Test public void testAddOnWifiUsabilityStatsListenerAndVerify() throws Exception { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.addOnWifiUsabilityStatsListener(mAppBinder, mOnWifiUsabilityStatsListener, TEST_WIFI_USABILITY_STATS_LISTENER_IDENTIFIER); mLooper.dispatchAll(); @@ -4139,8 +3899,6 @@ public class WifiServiceImplTest { */ @Test public void testRemoveOnWifiUsabilityStatsListenerAndVerify() throws Exception { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.removeOnWifiUsabilityStatsListener(0); mLooper.dispatchAll(); verify(mWifiMetrics).removeOnWifiUsabilityListener(0); @@ -4169,8 +3927,6 @@ public class WifiServiceImplTest { */ @Test public void testWifiUsabilityScoreUpdateAfterScoreEvent() { - setupClientModeImplHandlerForPost(); - mWifiServiceImpl.updateWifiUsabilityScore(anyInt(), anyInt(), 15); mLooper.dispatchAll(); verify(mClientModeImpl).updateWifiUsabilityScore(anyInt(), anyInt(), anyInt()); @@ -4182,31 +3938,19 @@ public class WifiServiceImplTest { .thenReturn(val); } - private void startLohsAndTethering(int apCount) { + private void startLohsAndTethering(int apCount) throws Exception { // initialization - setupClientModeImplHandlerForPost(); setupMaxApInterfaces(apCount); - mWifiServiceImpl.checkAndStartWifi(); - verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), - (IntentFilter) argThat(new IntentFilterMatcher())); + // For these tests, always use distinct interface names for LOHS and tethered. + mLohsInterfaceName = WIFI_IFACE_NAME2; - // start LOHS - registerLOHSRequestFull(); - String ifaceName = apCount >= 2 ? WIFI_IFACE_NAME2 : WIFI_IFACE_NAME; - mWifiServiceImpl.updateInterfaceIpState(ifaceName, IFACE_IP_MODE_LOCAL_ONLY); - mLooper.dispatchAll(); - verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(1), anyInt(), any(SoftApModeConfiguration.class)); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - assertEquals(HOTSPOT_STARTED, mMessageCaptor.getValue().what); - reset(mWifiController); - reset(mHandler); + setupLocalOnlyHotspot(); + reset(mActiveModeWarden); // start tethering boolean tetheringResult = mWifiServiceImpl.startSoftAp(null); assertTrue(tetheringResult); - verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(1), anyInt(), any(SoftApModeConfiguration.class)); + verify(mActiveModeWarden).startSoftAp(any()); mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); } @@ -4216,15 +3960,12 @@ public class WifiServiceImplTest { * doesn't support dual AP operation. */ @Test - public void testStartLohsAndTethering1AP() { + public void testStartLohsAndTethering1AP() throws Exception { startLohsAndTethering(1); // verify LOHS got stopped - mLooper.dispatchAll(); - verify(mHandler).handleMessage(mMessageCaptor.capture()); - assertEquals(HOTSPOT_FAILED, mMessageCaptor.getValue().what); - verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(0), eq(WifiManager.IFACE_IP_MODE_LOCAL_ONLY)); + verify(mLohsCallback).onHotspotFailed(anyInt()); + verify(mActiveModeWarden).stopSoftAp(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } /** @@ -4232,13 +3973,12 @@ public class WifiServiceImplTest { * that does support dual AP operation. */ @Test - public void testStartLohsAndTethering2AP() { + public void testStartLohsAndTethering2AP() throws Exception { startLohsAndTethering(2); // verify LOHS didn't get stopped - mLooper.dispatchAll(); - verify(mHandler, never()).handleMessage(any(Message.class)); - verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(0), anyInt()); + verifyZeroInteractions(ignoreStubs(mLohsCallback)); + verify(mActiveModeWarden, never()).stopSoftAp(anyInt()); } /** @@ -4271,4 +4011,196 @@ public class WifiServiceImplTest { } catch (RemoteException e) { } } + + /** + * Verifies that configs can be removed. + */ + @Test + public void testRemoveNetworkIsAllowedForAppsTargetingBelowQSdk() { + doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) + .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); + when(mWifiConfigManager.removeNetwork(eq(0), anyInt(), anyString())).thenReturn(true); + when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), + eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true); + + mLooper.startAutoDispatch(); + boolean result = mWifiServiceImpl.removeNetwork(0, TEST_PACKAGE_NAME); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + + assertTrue(result); + verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt(), anyString()); + } + + /** + * Verify that addOrUpdatePasspointConfig will redirect calls to {@link PasspointManager} + * and returning the result that's returned from {@link PasspointManager}. + */ + @Test + public void addOrUpdatePasspointConfig() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn("test.com"); + config.setHomeSp(homeSp); + + when(mPasspointManager.addOrUpdateProvider( + config, Binder.getCallingUid(), TEST_PACKAGE_NAME, false)) + .thenReturn(true); + mLooper.startAutoDispatch(); + assertTrue(mWifiServiceImpl.addOrUpdatePasspointConfiguration(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + reset(mPasspointManager); + + when(mPasspointManager.addOrUpdateProvider( + config, Binder.getCallingUid(), TEST_PACKAGE_NAME, false)) + .thenReturn(false); + mLooper.startAutoDispatch(); + assertFalse(mWifiServiceImpl.addOrUpdatePasspointConfiguration(config, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + } + /** + * Verify that removePasspointConfiguration will redirect calls to {@link PasspointManager} + * and returning the result that's returned from {@link PasspointManager}. + */ + @Test + public void removePasspointConfig() throws Exception { + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + + String fqdn = "test.com"; + when(mPasspointManager.removeProvider(anyInt(), anyBoolean(), eq(fqdn))).thenReturn(true); + mLooper.startAutoDispatch(); + assertTrue(mWifiServiceImpl.removePasspointConfiguration(fqdn, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + reset(mPasspointManager); + + when(mPasspointManager.removeProvider(anyInt(), anyBoolean(), eq(fqdn))).thenReturn(false); + mLooper.startAutoDispatch(); + assertFalse(mWifiServiceImpl.removePasspointConfiguration(fqdn, TEST_PACKAGE_NAME)); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + } + + /** + * Test that DISABLE_NETWORK returns failure to public API when WifiConfigManager returns + * failure. + */ + @Test + public void testDisableNetworkFailureAppBelowQSdk() throws Exception { + doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) + .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); + when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), + eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true); + when(mWifiConfigManager.disableNetwork(anyInt(), anyInt(), anyString())).thenReturn(false); + + mLooper.startAutoDispatch(); + boolean succeeded = mWifiServiceImpl.disableNetwork(0, TEST_PACKAGE_NAME); + mLooper.stopAutoDispatchAndIgnoreExceptions(); + assertFalse(succeeded); + } + + @Test + public void testAllowAutojoinFailureNoNetworkSettingsPermission() throws Exception { + doThrow(new SecurityException()).when(mContext) + .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + eq("WifiService")); + try { + mWifiServiceImpl.allowAutojoin(0, true); + fail("Expected SecurityException"); + } catch (SecurityException e) { + // Test succeeded + } + } + + /** + * Test handle boot completed sequence. + */ + @Test + public void testHandleBootCompleted() throws Exception { + when(mWifiInjector.getPasspointProvisionerHandlerThread()) + .thenReturn(mock(HandlerThread.class)); + mWifiServiceImpl.handleBootCompleted(); + mLooper.dispatchAll(); + + verify(mWifiConfigManager).loadFromStore(); + verify(mPasspointManager).initializeProvisioner(any()); + verify(mClientModeImpl).handleBootCompleted(); + } + + /** + * Test handle user switch sequence. + */ + @Test + public void testHandleUserSwitch() throws Exception { + mWifiServiceImpl.handleUserSwitch(5); + mLooper.dispatchAll(); + verify(mWifiConfigManager).handleUserSwitch(5); + } + + /** + * Test handle user unlock sequence. + */ + @Test + public void testHandleUserUnlock() throws Exception { + mWifiServiceImpl.handleUserUnlock(5); + mLooper.dispatchAll(); + verify(mWifiConfigManager).handleUserUnlock(5); + } + + /** + * Test handle user stop sequence. + */ + @Test + public void testHandleUserStop() throws Exception { + mWifiServiceImpl.handleUserStop(5); + mLooper.dispatchAll(); + verify(mWifiConfigManager).handleUserStop(5); + } + + /** + * Test register scan result listener without permission. + */ + @Test(expected = SecurityException.class) + public void testRegisterScanResultListenerWithMissingPermission() throws Exception { + doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.ACCESS_WIFI_STATE), eq("WifiService")); + final int listenerIdentifier = 1; + mWifiServiceImpl.registerScanResultsListener(mAppBinder, + mClientScanResultsListener, + listenerIdentifier); + } + + /** + * Test unregister scan result listener without permission. + */ + @Test(expected = SecurityException.class) + public void testUnregisterScanResultListenerWithMissingPermission() throws Exception { + doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.ACCESS_WIFI_STATE), eq("WifiService")); + final int listenerIdentifier = 1; + mWifiServiceImpl.unregisterScanResultsListener(listenerIdentifier); + } + + /** + * Test register scan result listener with illegal argument. + */ + @Test(expected = IllegalArgumentException.class) + public void testRegisterScanResultListenerWithIllegalArgument() throws Exception { + final int listenerIdentifier = 1; + mWifiServiceImpl.registerScanResultsListener(mAppBinder, null, listenerIdentifier); + } + + /** + * Test register and unregister listener will go to ScanRequestProxy; + */ + @Test + public void testRegisterUnregisterScanResultListener() throws Exception { + final int listenerIdentifier = 1; + mWifiServiceImpl.registerScanResultsListener(mAppBinder, + mClientScanResultsListener, + listenerIdentifier); + mLooper.dispatchAll(); + verify(mScanRequestProxy).registerScanResultsListener(eq(mAppBinder), + eq(mClientScanResultsListener), eq(listenerIdentifier)); + mWifiServiceImpl.unregisterScanResultsListener(listenerIdentifier); + mLooper.dispatchAll(); + verify(mScanRequestProxy).unregisterScanResultsListener(eq(listenerIdentifier)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiStateTrackerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiStateTrackerTest.java index 682ba5b32f..410178888d 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiStateTrackerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiStateTrackerTest.java @@ -34,7 +34,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiStateTracker}. */ @SmallTest -public class WifiStateTrackerTest { +public class WifiStateTrackerTest extends WifiBaseTest { private static final String TAG = "WifiStateTrackerTest"; @Mock IBatteryStats mBatteryStats; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java new file mode 100644 index 0000000000..59f2ed817b --- /dev/null +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.os.Handler; +import android.os.HandlerThread; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import java.util.function.Supplier; + +@SmallTest +public class WifiThreadRunnerTest { + + private static final int RESULT = 2; + private static final int VALUE_ON_TIMEOUT = -1; + + private WifiThreadRunner mWifiThreadRunner; + + @Mock private Runnable mRunnable; + + private Handler mHandler; + + @Spy private Supplier<Integer> mSupplier = () -> RESULT; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + HandlerThread mHandlerThread = new HandlerThread("WifiThreadRunnerTestHandlerThread"); + mHandlerThread.start(); + + // runWithScissors() is a final method that cannot be mocked, instead wrap it in a spy, + // then it can be mocked + mHandler = spy(mHandlerThread.getThreadHandler()); + + mWifiThreadRunner = new WifiThreadRunner(mHandler); + } + + @Test + public void callSuccess_returnExpectedValue() { + // doAnswer(<lambda>).when(<spy>).method(<args>) syntax is needed for spies instead of + // when(<mock>.method(<args>)).thenAnswer(<lambda>) for mocks + doAnswer(invocation -> { + Object[] args = invocation.getArguments(); + Runnable runnable = (Runnable) args[0]; + runnable.run(); + return true; + }).when(mHandler).runWithScissors(any(), anyLong()); + + Integer result = mWifiThreadRunner.call(mSupplier, VALUE_ON_TIMEOUT); + + assertThat(result).isEqualTo(RESULT); + verify(mSupplier).get(); + } + + @Test + public void callFailure_returnValueOnTimeout() { + doReturn(false).when(mHandler).post(any()); + + Integer result = mWifiThreadRunner.call(mSupplier, VALUE_ON_TIMEOUT); + + assertThat(result).isEqualTo(VALUE_ON_TIMEOUT); + verify(mSupplier, never()).get(); + } + + @Test + public void runSuccess() { + doAnswer(invocation -> { + Object[] args = invocation.getArguments(); + Runnable runnable = (Runnable) args[0]; + runnable.run(); + return true; + }).when(mHandler).runWithScissors(any(), anyLong()); + + boolean result = mWifiThreadRunner.run(mRunnable); + + assertThat(result).isTrue(); + verify(mRunnable).run(); + } + + @Test + public void runFailure() { + doReturn(false).when(mHandler).post(any()); + + boolean runSuccess = mWifiThreadRunner.run(mRunnable); + + assertThat(runSuccess).isFalse(); + verify(mRunnable, never()).run(); + } + + @Test + public void postSuccess() { + doReturn(true).when(mHandler).post(any()); + + boolean postSuccess = mWifiThreadRunner.post(mRunnable); + + assertThat(postSuccess).isTrue(); + verify(mHandler).post(mRunnable); + // assert that the runnable is not run on the calling thread + verify(mRunnable, never()).run(); + } + + @Test + public void postFailure() { + doReturn(false).when(mHandler).post(any()); + + boolean postSuccess = mWifiThreadRunner.post(mRunnable); + + assertThat(postSuccess).isFalse(); + verify(mHandler).post(mRunnable); + verify(mRunnable, never()).run(); + } +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiTrafficPollerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiTrafficPollerTest.java index ef49d2f4fa..1c7afef546 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiTrafficPollerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiTrafficPollerTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify; import android.net.wifi.ITrafficStateCallback; import android.net.wifi.WifiManager; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.test.TestLooper; @@ -40,7 +41,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiTrafficPoller}. */ @SmallTest -public class WifiTrafficPollerTest { +public class WifiTrafficPollerTest extends WifiBaseTest { public static final String TAG = "WifiTrafficPollerTest"; private TestLooper mLooper; @@ -63,7 +64,7 @@ public class WifiTrafficPollerTest { mLooper = new TestLooper(); MockitoAnnotations.initMocks(this); - mWifiTrafficPoller = new WifiTrafficPoller(mLooper.getLooper()); + mWifiTrafficPoller = new WifiTrafficPoller(new Handler(mLooper.getLooper())); // Set the current mTxPkts and mRxPkts to DEFAULT_PACKET_COUNT mWifiTrafficPoller.notifyOnDataActivity(DEFAULT_PACKET_COUNT, DEFAULT_PACKET_COUNT); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java index aa44023ae9..fc0cdd63b2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java @@ -87,7 +87,6 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.os.Handler; -import android.os.Looper; import android.os.RemoteException; import android.os.test.TestLooper; import android.system.OsConstants; @@ -119,7 +118,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.WifiVendorHal}. */ @SmallTest -public class WifiVendorHalTest { +public class WifiVendorHalTest extends WifiBaseTest { private static final String TEST_IFACE_NAME = "wlan0"; private static final String TEST_IFACE_NAME_1 = "wlan1"; @@ -128,20 +127,22 @@ public class WifiVendorHalTest { private static final int[] TEST_FREQUENCIES = {2412, 2417, 2422, 2427, 2432, 2437}; - WifiVendorHal mWifiVendorHal; + private WifiVendorHal mWifiVendorHal; private WifiStatus mWifiStatusSuccess; private WifiStatus mWifiStatusFailure; private WifiStatus mWifiStatusBusy; - WifiLog mWifiLog; + private WifiLog mWifiLog; + private TestLooper mLooper; + private Handler mHandler; @Mock private HalDeviceManager mHalDeviceManager; @Mock - private TestLooper mLooper; - @Mock private WifiVendorHal.HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks; @Mock private IWifiApIface mIWifiApIface; @Mock + private android.hardware.wifi.V1_4.IWifiApIface mIWifiApIfaceV14; + @Mock private IWifiChip mIWifiChip; @Mock private android.hardware.wifi.V1_1.IWifiChip mIWifiChipV11; @@ -168,8 +169,8 @@ public class WifiVendorHalTest { * device. */ private class WifiVendorHalSpyV1_1 extends WifiVendorHal { - WifiVendorHalSpyV1_1(HalDeviceManager halDeviceManager, Looper looper) { - super(halDeviceManager, looper); + WifiVendorHalSpyV1_1(HalDeviceManager halDeviceManager, Handler handler) { + super(halDeviceManager, handler); } @Override @@ -205,8 +206,8 @@ public class WifiVendorHalTest { * the 1.2 HAL running on the device. */ private class WifiVendorHalSpyV1_2 extends WifiVendorHal { - WifiVendorHalSpyV1_2(HalDeviceManager halDeviceManager, Looper looper) { - super(halDeviceManager, looper); + WifiVendorHalSpyV1_2(HalDeviceManager halDeviceManager, Handler handler) { + super(halDeviceManager, handler); } @Override @@ -242,8 +243,8 @@ public class WifiVendorHalTest { * the 1.3 HAL running on the device. */ private class WifiVendorHalSpyV1_3 extends WifiVendorHal { - WifiVendorHalSpyV1_3(HalDeviceManager halDeviceManager, Looper looper) { - super(halDeviceManager, looper); + WifiVendorHalSpyV1_3(HalDeviceManager halDeviceManager, Handler handler) { + super(halDeviceManager, handler); } @Override @@ -289,6 +290,7 @@ public class WifiVendorHalTest { MockitoAnnotations.initMocks(this); mWifiLog = new FakeWifiLog(); mLooper = new TestLooper(); + mHandler = new Handler(mLooper.getLooper()); mWifiStatusSuccess = new WifiStatus(); mWifiStatusSuccess.code = WifiStatusCode.SUCCESS; mWifiStatusFailure = new WifiStatus(); @@ -364,7 +366,7 @@ public class WifiVendorHalTest { }).when(mIWifiApIface).getName(any(IWifiIface.getNameCallback.class)); // Create the vendor HAL object under test. - mWifiVendorHal = new WifiVendorHal(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHal(mHalDeviceManager, mHandler); // Initialize the vendor HAL to capture the registered callback. mWifiVendorHal.initialize(mVendorHalDeathHandler); @@ -832,7 +834,7 @@ public class WifiVendorHalTest { } }).when(mIWifiStaIfaceV13).getFactoryMacAddress(any( android.hardware.wifi.V1_3.IWifiStaIface.getFactoryMacAddressCallback.class)); - mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mHandler); assertEquals(MacAddress.BROADCAST_ADDRESS.toString(), mWifiVendorHal.getFactoryMacAddress(TEST_IFACE_NAME).toString()); verify(mIWifiStaIfaceV13).getFactoryMacAddress(any()); @@ -868,7 +870,7 @@ public class WifiVendorHalTest { */ @Test public void testLinkLayerStatsCorrectVersionWithHalV1_3() throws Exception { - mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mHandler); mWifiVendorHal.getWifiLinkLayerStats(TEST_IFACE_NAME); verify(mIWifiStaIfaceV13).getLinkLayerStats_1_3(any()); } @@ -1308,7 +1310,7 @@ public class WifiVendorHalTest { @Test public void testReadApf() throws Exception { // Expose the 1.2 IWifiStaIface. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); byte[] program = new byte[] {65, 66, 67}; ArrayList<Byte> expected = new ArrayList<>(3); @@ -1473,7 +1475,7 @@ public class WifiVendorHalTest { */ @Test public void testFlushRingBufferToFile() throws Exception { - mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mHandler); when(mIWifiChipV13.flushRingBufferToFile()).thenReturn(mWifiStatusSuccess); assertFalse(mWifiVendorHal.flushRingBufferData()); @@ -2376,7 +2378,7 @@ public class WifiVendorHalTest { sarInfo.isVoiceCall = true; // Now expose the 1.1 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mHandler); when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2402,7 +2404,7 @@ public class WifiVendorHalTest { sarInfo.isVoiceCall = true; // Now expose the 1.2 IWifiChip - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2442,7 +2444,7 @@ public class WifiVendorHalTest { sarInfo.sarSensorSupported = false; // Now expose the 1.1 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mHandler); when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2468,7 +2470,7 @@ public class WifiVendorHalTest { sarInfo.sarSensorSupported = false; // Now expose the 1.1 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mHandler); when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2500,7 +2502,7 @@ public class WifiVendorHalTest { sarInfo.sarSensorSupported = false; // Now expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2526,7 +2528,7 @@ public class WifiVendorHalTest { sarInfo.sarSensorSupported = false; // Now expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2562,7 +2564,7 @@ public class WifiVendorHalTest { sarInfo.isWifiSapEnabled = true; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); // ON_BODY_CELL_ON @@ -2591,7 +2593,7 @@ public class WifiVendorHalTest { sarInfo.isVoiceCall = true; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); // ON_HEAD_CELL_ON @@ -2620,7 +2622,7 @@ public class WifiVendorHalTest { sarInfo.isEarPieceActive = true; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); // ON_HEAD_CELL_ON @@ -2647,7 +2649,7 @@ public class WifiVendorHalTest { sarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); // ON_HEAD_CELL_OFF @@ -2677,7 +2679,7 @@ public class WifiVendorHalTest { sarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; // Now expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2713,7 +2715,7 @@ public class WifiVendorHalTest { sarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HAND; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2748,7 +2750,7 @@ public class WifiVendorHalTest { sarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; // Expose the 1.1 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mHandler); when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess); when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); @@ -2783,7 +2785,7 @@ public class WifiVendorHalTest { sarInfo.sensorState = SAR_SENSOR_INVALID_STATE; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); assertTrue(mWifiVendorHal.startVendorHalSta()); assertFalse(mWifiVendorHal.selectTxPowerScenario(sarInfo)); @@ -2813,7 +2815,7 @@ public class WifiVendorHalTest { sarInfo.isVoiceCall = false; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2845,7 +2847,7 @@ public class WifiVendorHalTest { sarInfo.isVoiceCall = true; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2877,7 +2879,7 @@ public class WifiVendorHalTest { sarInfo.isVoiceCall = false; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2907,7 +2909,7 @@ public class WifiVendorHalTest { sarInfo.isVoiceCall = false; // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); @@ -2925,7 +2927,7 @@ public class WifiVendorHalTest { @Test public void testSetLowLatencyMode_1_2() throws RemoteException { // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); assertFalse(mWifiVendorHal.setLowLatencyMode(true)); assertFalse(mWifiVendorHal.setLowLatencyMode(false)); } @@ -2938,7 +2940,7 @@ public class WifiVendorHalTest { int mode = android.hardware.wifi.V1_3.IWifiChip.LatencyMode.LOW; // Expose the 1.3 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mHandler); when(mIWifiChipV13.setLatencyMode(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.setLowLatencyMode(true)); verify(mIWifiChipV13).setLatencyMode(eq(mode)); @@ -2952,7 +2954,7 @@ public class WifiVendorHalTest { int mode = android.hardware.wifi.V1_3.IWifiChip.LatencyMode.NORMAL; // Expose the 1.3 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_3(mHalDeviceManager, mHandler); when(mIWifiChipV13.setLatencyMode(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.setLowLatencyMode(false)); verify(mIWifiChipV13).setLatencyMode(eq(mode)); @@ -3024,7 +3026,7 @@ public class WifiVendorHalTest { @Test public void testAlertCallbackUsing_1_2_EventCallback() throws Exception { // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); assertTrue(mWifiVendorHal.startVendorHalSta()); assertNotNull(mIWifiChipEventCallbackV12); @@ -3036,9 +3038,9 @@ public class WifiVendorHalTest { * Verifies setMacAddress() success. */ @Test - public void testSetMacAddressSuccess() throws Exception { + public void testSetStaMacAddressSuccess() throws Exception { // Expose the 1.2 IWifiStaIface. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); byte[] macByteArray = TEST_MAC_ADDRESS.toByteArray(); when(mIWifiStaIfaceV12.setMacAddress(macByteArray)).thenReturn(mWifiStatusSuccess); @@ -3050,9 +3052,9 @@ public class WifiVendorHalTest { * Verifies setMacAddress() can handle failure status. */ @Test - public void testSetMacAddressFailDueToStatusFailure() throws Exception { + public void testSetStaMacAddressFailDueToStatusFailure() throws Exception { // Expose the 1.2 IWifiStaIface. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); byte[] macByteArray = TEST_MAC_ADDRESS.toByteArray(); when(mIWifiStaIfaceV12.setMacAddress(macByteArray)).thenReturn(mWifiStatusFailure); @@ -3064,9 +3066,9 @@ public class WifiVendorHalTest { * Verifies setMacAddress() can handle RemoteException. */ @Test - public void testSetMacAddressFailDueToRemoteException() throws Exception { + public void testSetStaMacAddressFailDueToRemoteException() throws Exception { // Expose the 1.2 IWifiStaIface. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); byte[] macByteArray = TEST_MAC_ADDRESS.toByteArray(); doThrow(new RemoteException()).when(mIWifiStaIfaceV12).setMacAddress(macByteArray); @@ -3075,6 +3077,51 @@ public class WifiVendorHalTest { } /** + * Verifies setMacAddress() success. + */ + @Test + public void testSetApMacAddressSuccess() throws Exception { + mWifiVendorHal = spy(mWifiVendorHal); + when(mWifiVendorHal.getWifiApIfaceForV1_4Mockable(TEST_IFACE_NAME_1)) + .thenReturn(mIWifiApIfaceV14); + byte[] macByteArray = TEST_MAC_ADDRESS.toByteArray(); + when(mIWifiApIfaceV14.setMacAddress(macByteArray)).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.setMacAddress(TEST_IFACE_NAME_1, TEST_MAC_ADDRESS)); + verify(mIWifiApIfaceV14).setMacAddress(macByteArray); + } + + /** + * Verifies setMacAddress() can handle failure status. + */ + @Test + public void testSetApMacAddressFailDueToStatusFailure() throws Exception { + mWifiVendorHal = spy(mWifiVendorHal); + when(mWifiVendorHal.getWifiApIfaceForV1_4Mockable(TEST_IFACE_NAME_1)) + .thenReturn(mIWifiApIfaceV14); + byte[] macByteArray = TEST_MAC_ADDRESS.toByteArray(); + when(mIWifiApIfaceV14.setMacAddress(macByteArray)).thenReturn(mWifiStatusFailure); + + assertFalse(mWifiVendorHal.setMacAddress(TEST_IFACE_NAME_1, TEST_MAC_ADDRESS)); + verify(mIWifiApIfaceV14).setMacAddress(macByteArray); + } + + /** + * Verifies setMacAddress() can handle RemoteException. + */ + @Test + public void testSetApMacAddressFailDueToRemoteException() throws Exception { + mWifiVendorHal = spy(mWifiVendorHal); + when(mWifiVendorHal.getWifiApIfaceForV1_4Mockable(TEST_IFACE_NAME_1)) + .thenReturn(mIWifiApIfaceV14); + byte[] macByteArray = TEST_MAC_ADDRESS.toByteArray(); + doThrow(new RemoteException()).when(mIWifiApIfaceV14).setMacAddress(macByteArray); + + assertFalse(mWifiVendorHal.setMacAddress(TEST_IFACE_NAME_1, TEST_MAC_ADDRESS)); + verify(mIWifiApIfaceV14).setMacAddress(macByteArray); + } + + /** * Verifies setMacAddress() does not crash with older HALs. */ @Test @@ -3242,7 +3289,7 @@ public class WifiVendorHalTest { private void startHalInStaModeAndRegisterRadioModeChangeCallback() { // Expose the 1.2 IWifiChip. - mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mHandler); mWifiVendorHal.registerRadioModeChangeHandler(mVendorHalRadioModeChangeHandler); assertTrue(mWifiVendorHal.startVendorHalSta()); assertNotNull(mIWifiChipEventCallbackV12); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiWakeMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiWakeMetricsTest.java index de5f380514..a27b141a14 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiWakeMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiWakeMetricsTest.java @@ -30,7 +30,7 @@ import org.junit.Before; import org.junit.Test; @SmallTest -public class WifiWakeMetricsTest { +public class WifiWakeMetricsTest extends WifiBaseTest { private WifiWakeMetrics mWifiWakeMetrics; diff --git a/service/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java b/service/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java index ca48f38aea..dadab98d65 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java @@ -39,14 +39,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.AlarmManager; -import android.net.wifi.IApInterface; -import android.net.wifi.IApInterfaceEventCallback; -import android.net.wifi.IClientInterface; -import android.net.wifi.IPnoScanEvent; -import android.net.wifi.IScanEvent; -import android.net.wifi.ISendMgmtFrameEvent; -import android.net.wifi.IWifiScannerImpl; -import android.net.wifi.IWificond; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; @@ -62,7 +54,16 @@ import com.android.server.wifi.WifiNative.SendMgmtFrameCallback; 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.IApInterface; +import com.android.server.wifi.wificond.IApInterfaceEventCallback; +import com.android.server.wifi.wificond.IClientInterface; +import com.android.server.wifi.wificond.IPnoScanEvent; +import com.android.server.wifi.wificond.IScanEvent; +import com.android.server.wifi.wificond.ISendMgmtFrameEvent; +import com.android.server.wifi.wificond.IWifiScannerImpl; +import com.android.server.wifi.wificond.IWificond; import com.android.server.wifi.wificond.NativeScanResult; +import com.android.server.wifi.wificond.NativeWifiClient; import com.android.server.wifi.wificond.PnoSettings; import com.android.server.wifi.wificond.RadioChainInfo; import com.android.server.wifi.wificond.SingleScanSettings; @@ -79,7 +80,6 @@ import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -88,7 +88,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.WificondControl}. */ @SmallTest -public class WificondControlTest { +public class WificondControlTest extends WifiBaseTest { @Mock private WifiInjector mWifiInjector; @Mock private WifiMonitor mWifiMonitor; @Mock private WifiMetrics mWifiMetrics; @@ -138,19 +138,22 @@ public class WificondControlTest { 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 int TEST_CAPABILITY = 0b0000_0000_0010_0100; 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_SSID; - frequency = TEST_FREQUENCY; - signalMbm = TEST_SIGNAL_MBM; - capability = TEST_CAPABILITY; - associated = TEST_ASSOCIATED; - radioChainInfos = new ArrayList<>(); - }}; + private static final NativeScanResult MOCK_NATIVE_SCAN_RESULT = createMockNativeScanResult(); + private static NativeScanResult createMockNativeScanResult() { + NativeScanResult result = new NativeScanResult(); + result.ssid = TEST_SSID; + result.bssid = TEST_BSSID; + result.infoElement = TEST_INFO_ELEMENT_SSID; + result.frequency = TEST_FREQUENCY; + result.signalMbm = TEST_SIGNAL_MBM; + result.tsf = TEST_TSF; + result.capability = TEST_CAPABILITY; + result.associated = TEST_ASSOCIATED; + result.radioChainInfos = new RadioChainInfo[0]; + return result; + } private static final RadioChainInfo MOCK_NATIVE_RADIO_CHAIN_INFO_1 = new RadioChainInfo() {{ chainId = 1; @@ -221,7 +224,7 @@ public class WificondControlTest { when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); mLooper = new TestLooper(); mWificondControl = new WificondControl(mWifiInjector, mWifiMonitor, mCarrierNetworkConfig, - mAlarmManager, mLooper.getLooper(), mClock); + mAlarmManager, new Handler(mLooper.getLooper()), mClock); assertEquals(mClientInterface, mWificondControl.setupInterfaceForClientMode( TEST_INTERFACE_NAME)); verify(mWifiInjector).makeWificond(); @@ -639,7 +642,7 @@ public class WificondControlTest { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(TEST_INFO_ELEMENT_SSID); out.write(TEST_INFO_ELEMENT_RSN); - NativeScanResult nativeScanResult = new NativeScanResult(MOCK_NATIVE_SCAN_RESULT); + NativeScanResult nativeScanResult = createMockNativeScanResult(); nativeScanResult.infoElement = out.toByteArray(); when(mWifiScannerImpl.getScanResults()).thenReturn( new NativeScanResult[] {nativeScanResult}); @@ -687,12 +690,10 @@ public class WificondControlTest { assertNotNull(mWifiScannerImpl); // Mock the returned array of NativeScanResult. - NativeScanResult nativeScanResult = new NativeScanResult(MOCK_NATIVE_SCAN_RESULT); + NativeScanResult nativeScanResult = createMockNativeScanResult(); // Add radio chain info - ArrayList<RadioChainInfo> nativeRadioChainInfos = new ArrayList<RadioChainInfo>() {{ - add(MOCK_NATIVE_RADIO_CHAIN_INFO_1); - add(MOCK_NATIVE_RADIO_CHAIN_INFO_2); - }}; + RadioChainInfo[] nativeRadioChainInfos = + { MOCK_NATIVE_RADIO_CHAIN_INFO_1, MOCK_NATIVE_RADIO_CHAIN_INFO_2 }; nativeScanResult.radioChainInfos = nativeRadioChainInfos; NativeScanResult[] mockScanResults = { nativeScanResult }; @@ -880,12 +881,6 @@ public class WificondControlTest { pnoScanEvent.OnPnoScanFailed(); verify(mWifiMetrics).incrementPnoScanFailedCount(); - - pnoScanEvent.OnPnoScanOverOffloadStarted(); - verify(mWifiMetrics).incrementPnoScanStartedOverOffloadCount(); - - pnoScanEvent.OnPnoScanOverOffloadFailed(0); - verify(mWifiMetrics).incrementPnoScanFailedOverOffloadCount(); } /** @@ -928,9 +923,9 @@ public class WificondControlTest { TEST_INTERFACE_NAME, mSoftApListener)); verify(mApInterface).registerCallback(apInterfaceCallbackCaptor.capture()); - int numStations = 5; - apInterfaceCallbackCaptor.getValue().onNumAssociatedStationsChanged(numStations); - verify(mSoftApListener).onNumAssociatedStationsChanged(eq(numStations)); + final NativeWifiClient[] testClients = new NativeWifiClient[]{}; + apInterfaceCallbackCaptor.getValue().onConnectedClientsChanged(testClients); + verify(mSoftApListener).onConnectedClientsChanged(Arrays.asList(testClients)); int channelFrequency = 2437; int channelBandwidth = IApInterfaceEventCallback.BANDWIDTH_20; @@ -1250,13 +1245,16 @@ public class WificondControlTest { verify(mSendMgmtFrameCallback).onFailure(eq(WifiNative.SEND_MGMT_FRAME_ERROR_TIMEOUT)); } - private void assertRadioChainInfosEqual( - List<RadioChainInfo> expected, android.net.wifi.ScanResult.RadioChainInfo[] actual) { - assertEquals(expected.size(), actual.length); - for (int i = 0; i < actual.length; i++) { - RadioChainInfo nativeRadioChainInfo = - new RadioChainInfo(actual[i].id, actual[i].level); - assertTrue(expected.contains(nativeRadioChainInfo)); + private void assertRadioChainInfosEqual(RadioChainInfo[] expected, + android.net.wifi.ScanResult.RadioChainInfo[] actual) { + assertEquals(expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + RadioChainInfo expectedInfo = expected[i]; + android.net.wifi.ScanResult.RadioChainInfo actualInfo = actual[i]; + assertEquals(String.format("unexpected chainId at index %d", i), + expectedInfo.chainId, actualInfo.id); + assertEquals(String.format("unexpected level at index %d", i), + expectedInfo.level, actualInfo.level); } } @@ -1277,8 +1275,8 @@ public class WificondControlTest { if (settings.scanType != mExpectedScanType) { return false; } - ArrayList<ChannelSettings> channelSettings = settings.channelSettings; - ArrayList<HiddenNetwork> hiddenNetworks = settings.hiddenNetworks; + ChannelSettings[] channelSettings = settings.channelSettings; + HiddenNetwork[] hiddenNetworks = settings.hiddenNetworks; if (mExpectedFreqs != null) { Set<Integer> freqSet = new HashSet<Integer>(); for (ChannelSettings channel : channelSettings) { @@ -1288,7 +1286,7 @@ public class WificondControlTest { return false; } } else { - if (channelSettings != null && channelSettings.size() > 0) { + if (channelSettings != null && channelSettings.length > 0) { return false; } } @@ -1304,7 +1302,7 @@ public class WificondControlTest { } } else { - if (hiddenNetworks != null && hiddenNetworks.size() > 0) { + if (hiddenNetworks != null && hiddenNetworks.length > 0) { return false; } } @@ -1338,22 +1336,22 @@ public class WificondControlTest { if (settings.pnoNetworks == null || mExpectedPnoSettings.networkList == null) { return false; } - if (settings.pnoNetworks.size() != mExpectedPnoSettings.networkList.length) { + if (settings.pnoNetworks.length != mExpectedPnoSettings.networkList.length) { return false; } - for (int i = 0; i < settings.pnoNetworks.size(); i++) { + for (int i = 0; i < settings.pnoNetworks.length; i++) { if (!mExpectedPnoSettings.networkList[i].ssid.equals(NativeUtil.encodeSsid( - NativeUtil.byteArrayToArrayList(settings.pnoNetworks.get(i).ssid)))) { + NativeUtil.byteArrayToArrayList(settings.pnoNetworks[i].ssid)))) { return false; } boolean isNetworkHidden = (mExpectedPnoSettings.networkList[i].flags & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0; - if (isNetworkHidden != settings.pnoNetworks.get(i).isHidden) { + if (isNetworkHidden != settings.pnoNetworks[i].isHidden) { return false; } if (!Arrays.equals(mExpectedPnoSettings.networkList[i].frequencies, - settings.pnoNetworks.get(i).frequencies)) { + settings.pnoNetworks[i].frequencies)) { return false; } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java b/service/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java index 3d011da23f..28fbc9371d 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java @@ -41,7 +41,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WrongPasswordNotifier}. */ @SmallTest -public class WrongPasswordNotifierTest { +public class WrongPasswordNotifierTest extends WifiBaseTest { private static final String TEST_SSID = "Test SSID"; @Mock Context mContext; diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java index 35916d2f6b..b803dd5156 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java @@ -81,6 +81,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.AsyncChannel; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; @@ -107,7 +108,7 @@ import java.util.Set; * Unit test harness for WifiAwareDataPathStateManager class. */ @SmallTest -public class WifiAwareDataPathStateManagerTest { +public class WifiAwareDataPathStateManagerTest extends WifiBaseTest { private static final String sAwareInterfacePrefix = "aware_data"; private TestLooper mMockLooper; @@ -164,6 +165,7 @@ public class WifiAwareDataPathStateManagerTest { when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), anyInt(), anyInt())) .thenReturn(true); when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true); mDut = new WifiAwareStateManager(); mDut.setNative(mMockNativeManager, mMockNative); diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java index 95ca739af0..47772bd7e9 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java @@ -33,6 +33,7 @@ import android.util.SparseIntArray; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.nano.WifiMetricsProto; import com.android.server.wifi.util.MetricsUtils; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -53,7 +54,7 @@ import java.util.Map; * Unit test harness for WifiAwareMetrics */ @SmallTest -public class WifiAwareMetricsTest { +public class WifiAwareMetricsTest extends WifiBaseTest { @Mock Clock mClock; @Mock private Context mMockContext; @Mock private AppOpsManager mMockAppOpsManager; @@ -291,6 +292,15 @@ public class WifiAwareMetricsTest { log.maxConcurrentAttachSessionsInApp, equalTo(2)); collector.checkThat("histogramAttachSessionStatus.length", log.histogramAttachSessionStatus.length, equalTo(3)); // 3 buckets + validateNanStatusProtoHistBucket("Bucket[SUCCESS]", + log.histogramAttachSessionStatus[0], + WifiMetricsProto.WifiAwareLog.SUCCESS, 5); + validateNanStatusProtoHistBucket("Bucket[INTERNAL_FAILURE]", + log.histogramAttachSessionStatus[1], + WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE, 2); + validateNanStatusProtoHistBucket("Bucket[UNKNOWN_HAL_STATUS]", + log.histogramAttachSessionStatus[2], + WifiMetricsProto.WifiAwareLog.UNKNOWN_HAL_STATUS, 1); collector.checkThat("histogramAttachDurationMs.length", log.histogramAttachDurationMs.length, equalTo(2)); validateProtoHistBucket("Duration[0]", log.histogramAttachDurationMs[0], 5, 6, 1); @@ -403,8 +413,23 @@ public class WifiAwareMetricsTest { log.maxConcurrentDiscoverySessionsInSystem, equalTo(8)); collector.checkThat("histogramPublishStatus.length", log.histogramPublishStatus.length, equalTo(2)); // 2 buckets + validateNanStatusProtoHistBucket("Bucket[SUCCESS]", + log.histogramPublishStatus[0], + WifiMetricsProto.WifiAwareLog.SUCCESS, 3); + validateNanStatusProtoHistBucket("Bucket[INTERNAL_FAILURE]", + log.histogramPublishStatus[1], + WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE, 1); collector.checkThat("histogramSubscribeStatus.length", log.histogramSubscribeStatus.length, equalTo(3)); // 3 buckets + validateNanStatusProtoHistBucket("Bucket[SUCCESS]", + log.histogramSubscribeStatus[0], + WifiMetricsProto.WifiAwareLog.SUCCESS, 5); + validateNanStatusProtoHistBucket("Bucket[INTERNAL_FAILURE]", + log.histogramSubscribeStatus[1], + WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE, 1); + validateNanStatusProtoHistBucket("Bucket[NO_RESOURCES_AVAILABLE]", + log.histogramSubscribeStatus[2], + WifiMetricsProto.WifiAwareLog.NO_RESOURCES_AVAILABLE, 1); collector.checkThat("numAppsWithDiscoverySessionFailureOutOfResources", log.numAppsWithDiscoverySessionFailureOutOfResources, equalTo(1)); validateProtoHistBucket("Publish Duration[0]", log.histogramPublishSessionDurationMs[0], 5, @@ -510,8 +535,20 @@ public class WifiAwareMetricsTest { collector.checkThat("maxConcurrentNdpPerNdi", log.maxConcurrentNdpPerNdi, equalTo(3)); collector.checkThat("histogramRequestNdpStatus.length", log.histogramRequestNdpStatus.length, equalTo(3)); + validateNanStatusProtoHistBucket("Bucket[SUCCESS]", + log.histogramRequestNdpStatus[0], + WifiMetricsProto.WifiAwareLog.SUCCESS, 3); + validateNanStatusProtoHistBucket("Bucket[INTERNAL_FAILURE]", + log.histogramRequestNdpStatus[1], + WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE, 2); + validateNanStatusProtoHistBucket("Bucket[UNKNOWN_HAL_STATUS]", + log.histogramRequestNdpStatus[2], + WifiMetricsProto.WifiAwareLog.NO_RESOURCES_AVAILABLE, 1); collector.checkThat("histogramRequestNdpOobStatus.length", log.histogramRequestNdpOobStatus.length, equalTo(1)); + validateNanStatusProtoHistBucket("Bucket[SUCCESS]", + log.histogramRequestNdpOobStatus[0], + WifiMetricsProto.WifiAwareLog.SUCCESS, 2); collector.checkThat("ndpCreationTimeMsMin", log.ndpCreationTimeMsMin, equalTo(1L)); collector.checkThat("ndpCreationTimeMsMax", log.ndpCreationTimeMsMax, equalTo(15L)); diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeApiTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeApiTest.java index 1be56cc9eb..0a30228cff 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeApiTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeApiTest.java @@ -48,6 +48,8 @@ import android.util.Pair; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -64,7 +66,7 @@ import java.util.ArrayList; * Unit test harness for WifiAwareNativeApi */ @SmallTest -public class WifiAwareNativeApiTest { +public class WifiAwareNativeApiTest extends WifiBaseTest { @Mock WifiAwareNativeManager mWifiAwareNativeManagerMock; @Mock IWifiNanIface mIWifiNanIfaceMock; @Mock android.hardware.wifi.V1_2.IWifiNanIface mIWifiNanIface12Mock; diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java index 140e7e43c5..d97ae06044 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java @@ -18,6 +18,7 @@ package com.android.server.wifi.aware; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; @@ -34,6 +35,7 @@ import android.os.Handler; import androidx.test.filters.SmallTest; import com.android.server.wifi.HalDeviceManager; +import com.android.server.wifi.WifiBaseTest; import org.junit.Before; import org.junit.Rule; @@ -48,7 +50,7 @@ import org.mockito.MockitoAnnotations; * Unit test harness for WifiAwareNativeManager. */ @SmallTest -public class WifiAwareNativeManagerTest { +public class WifiAwareNativeManagerTest extends WifiBaseTest { private WifiAwareNativeManager mDut; @Mock private WifiAwareStateManager mWifiAwareStateManagerMock; @Mock private HalDeviceManager mHalDeviceManager; @@ -289,5 +291,9 @@ public class WifiAwareNativeManagerTest { any()); mInOrder.verify(mIWifiNanIface12Mock).registerEventCallback_1_2(any()); assertEquals("Interface mismatch", mIWifiNanIface12Mock, mDut.getWifiNanIface()); + + // 3. receive Availability Change, has interface, should ignore + mAvailListenerCaptor.getValue().onAvailabilityChanged(false); + assertTrue("AwareNativeAvailable mismatch ", mDut.isAwareNativeAvailable()); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java index 1204c4f815..95b7b0b14f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java @@ -51,6 +51,7 @@ import android.util.SparseIntArray; import androidx.test.filters.SmallTest; import com.android.server.wifi.FrameworkFacade; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; @@ -71,7 +72,7 @@ import java.util.Map; * Unit test harness for WifiAwareStateManager. */ @SmallTest -public class WifiAwareServiceImplTest { +public class WifiAwareServiceImplTest extends WifiBaseTest { private static final int MAX_LENGTH = 255; private WifiAwareServiceImplSpy mDut; @@ -553,8 +554,8 @@ public class WifiAwareServiceImplTest { mDut.sendMessage(clientId, sessionId, peerId, message, messageId, 0); - verify(mAwareStateManagerMock).sendMessage(clientId, sessionId, peerId, message, messageId, - 0); + verify(mAwareStateManagerMock).sendMessage(anyInt(), eq(clientId), eq(sessionId), + eq(peerId), eq(message), eq(messageId), eq(0)); } /** @@ -570,8 +571,8 @@ public class WifiAwareServiceImplTest { mDut.sendMessage(clientId, sessionId, peerId, message, messageId, 0); - verify(mAwareStateManagerMock).sendMessage(clientId, sessionId, peerId, message, messageId, - 0); + verify(mAwareStateManagerMock).sendMessage(anyInt(), eq(clientId), eq(sessionId), + eq(peerId), eq(message), eq(messageId), eq(0)); } @Test diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java index d6b1b38d14..d5948282d5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java @@ -74,6 +74,7 @@ import android.util.SparseArray; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; @@ -107,7 +108,7 @@ import java.util.Set; * Unit test harness for WifiAwareStateManager. */ @SmallTest -public class WifiAwareStateManagerTest { +public class WifiAwareStateManagerTest extends WifiBaseTest { private TestLooper mMockLooper; private Random mRandomNg = new Random(15687); private WifiAwareStateManager mDut; @@ -165,6 +166,7 @@ public class WifiAwareStateManagerTest { when(mMockPowerManager.isDeviceIdleMode()).thenReturn(false); when(mMockPowerManager.isInteractive()).thenReturn(true); when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true); ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass( BroadcastReceiver.class); @@ -1437,8 +1439,8 @@ public class WifiAwareStateManagerTest { peerMsg.getBytes()); // (4) message Tx successful queuing - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + ssi.getBytes(), messageId, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId)); @@ -1447,8 +1449,8 @@ public class WifiAwareStateManagerTest { mMockLooper.dispatchAll(); // (5) message Tx successful queuing - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId2, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + ssi.getBytes(), messageId2, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId2)); @@ -1547,7 +1549,7 @@ public class WifiAwareStateManagerTest { int peerId2 = peerIdCaptor.getValue(); // (4) sending messages back to same peers: one Tx fails, other succeeds - mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(), + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(), msgToPeerId2, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), @@ -1556,7 +1558,7 @@ public class WifiAwareStateManagerTest { mDut.onMessageSendQueuedSuccessResponse(transactionIdVal); mDut.onMessageSendSuccessNotification(transactionIdVal); - mDut.sendMessage(clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(), + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(), msgToPeerId1, 0); mMockLooper.dispatchAll(); inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2); @@ -1641,8 +1643,8 @@ public class WifiAwareStateManagerTest { mMockLooper.dispatchAll(); inOrder.verify(mockSessionCallback).onMessageReceived(peerId.capture(), eq(msgFromPeer1.getBytes())); - mDut.sendMessage(clientId, sessionId.getValue(), peerId.getValue(), msgToPeer1.getBytes(), - msgToPeerId1, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId.getValue(), + msgToPeer1.getBytes(), msgToPeerId1, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(requestorId), eq(peerMacOrig), eq(msgToPeer1.getBytes()), @@ -1659,8 +1661,8 @@ public class WifiAwareStateManagerTest { mMockLooper.dispatchAll(); inOrder.verify(mockSessionCallback).onMessageReceived(peerId.capture(), eq(msgFromPeer2.getBytes())); - mDut.sendMessage(clientId, sessionId.getValue(), peerId.getValue(), msgToPeer2.getBytes(), - msgToPeerId2, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId.getValue(), + msgToPeer2.getBytes(), msgToPeerId2, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(requestorId), eq(peerMacLater), eq(msgToPeer2.getBytes()), @@ -1732,7 +1734,7 @@ public class WifiAwareStateManagerTest { eq(peerMatchFilter.getBytes())); // (3) send message to invalid peer ID - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue() + 5, + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue() + 5, ssi.getBytes(), messageId, 0); mMockLooper.dispatchAll(); inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, @@ -1801,8 +1803,8 @@ public class WifiAwareStateManagerTest { eq(peerMatchFilter.getBytes())); // (3) send 2 messages and enqueue successfully - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + ssi.getBytes(), messageId, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId)); @@ -1810,8 +1812,8 @@ public class WifiAwareStateManagerTest { mDut.onMessageSendQueuedSuccessResponse(transactionId1); mMockLooper.dispatchAll(); - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId + 1, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + 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)); @@ -1820,8 +1822,8 @@ public class WifiAwareStateManagerTest { mMockLooper.dispatchAll(); // (4) send a message and get a queueing failure (not queue full) - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId + 2, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + ssi.getBytes(), messageId + 2, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 2)); @@ -1836,8 +1838,8 @@ public class WifiAwareStateManagerTest { when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(), any(), anyInt())).thenReturn(false); - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId + 3, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + ssi.getBytes(), messageId + 3, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 3)); @@ -1927,8 +1929,8 @@ public class WifiAwareStateManagerTest { eq(peerMatchFilter.getBytes())); // (3) send message and enqueue successfully - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId, retryCount); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + ssi.getBytes(), messageId, retryCount); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId)); @@ -2014,8 +2016,8 @@ public class WifiAwareStateManagerTest { eq(peerMatchFilter.getBytes())); // (3) send message and enqueue successfully - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(), - messageId, retryCount); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + ssi.getBytes(), messageId, retryCount); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId)); @@ -2110,7 +2112,7 @@ public class WifiAwareStateManagerTest { int remainingMessages = numberOfMessages; for (int i = 0; i < numberOfMessages; ++i) { - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), null, + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), null, messageIdBase + i, 0); mMockLooper.dispatchAll(); // at 1/2 interval have the system simulate transmitting a queued message over-the-air @@ -2137,6 +2139,153 @@ public class WifiAwareStateManagerTest { } /** + * Validate that the message queue depth per process function. Tests the case + * with two processes both have message num larger than queue depth. And all messages get + * into the firmware queue are sent out and are received on first attempt. + */ + @Test + public void testSendMessageQueueLimitBlock() throws Exception { + final int clientId1 = 1005; + final int clientId2 = 1006; + final int uid1 = 1000; + final int uid2 = 1500; + final int pid1 = 2000; + final int pid2 = 3000; + final String callingPackage1 = "com.google.somePackage1"; + final String callingPackage2 = "com.google.somePackage2"; + final String serviceName1 = "some-service-name1"; + final String serviceName2 = "some-service-name2"; + final byte subscribeId1 = 15; + final byte subscribeId2 = 16; + final int requestorId1 = 22; + final int requestorId2 = 23; + final byte[] peerMac1 = HexEncoding.decode("060708090A0B".toCharArray(), false); + final byte[] peerMac2 = HexEncoding.decode("060708090A0C".toCharArray(), false); + final int messageIdBase1 = 6948; + final int messageIdBase2 = 7948; + final int numberOfMessages = 70; + final int queueDepth = 6; + final int messageQueueDepthPerUid = 50; + final int numOfReject = numberOfMessages - messageQueueDepthPerUid; + + ConfigRequest configRequest1 = new ConfigRequest.Builder().build(); + SubscribeConfig subscribeConfig1 = new SubscribeConfig.Builder() + .setServiceName(serviceName1).build(); + ConfigRequest configRequest2 = new ConfigRequest.Builder().build(); + SubscribeConfig subscribeConfig2 = new SubscribeConfig.Builder() + .setServiceName(serviceName2).build(); + + IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class); + IWifiAwareDiscoverySessionCallback mockSessionCallback = mock( + IWifiAwareDiscoverySessionCallback.class); + ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); + ArgumentCaptor<Integer> sessionId1 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> sessionId2 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> messageIdCaptorFail = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> messageIdCaptorSuccess = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> peerIdCaptor1 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> peerIdCaptor2 = 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(clientId1, uid1, pid1, callingPackage1, mockCallback, configRequest1, false); + mMockLooper.dispatchAll(); + inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), + eq(configRequest1), eq(false), eq(true), eq(true), eq(false)); + mDut.onConfigSuccessResponse(transactionId.getValue()); + mMockLooper.dispatchAll(); + inOrder.verify(mockCallback).onConnectSuccess(clientId1); + + mDut.connect(clientId2, uid2, pid2, callingPackage2, mockCallback, configRequest2, false); + mMockLooper.dispatchAll(); + inOrder.verify(mockCallback).onConnectSuccess(clientId2); + + // (1) subscribe + mDut.subscribe(clientId1, subscribeConfig1, mockSessionCallback); + mMockLooper.dispatchAll(); + inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0), + eq(subscribeConfig1)); + mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId1); + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback).onSessionStarted(sessionId1.capture()); + + mDut.subscribe(clientId2, subscribeConfig2, mockSessionCallback); + mMockLooper.dispatchAll(); + inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0), + eq(subscribeConfig2)); + mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId2); + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback).onSessionStarted(sessionId2.capture()); + + // (2) match + mDut.onMatchNotification(subscribeId1, requestorId1, peerMac1, null, null, 0, 0); + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor1.capture(), isNull(), isNull()); + + mDut.onMatchNotification(subscribeId2, requestorId2, peerMac2, null, null, 0, 0); + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor2.capture(), isNull(), isNull()); + + // (3) Enqueue messages + SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth, + null, null, null); + when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(), + any(), anyInt())).thenAnswer(answerObj); + + for (int i = 0; i < numberOfMessages; ++i) { + mDut.sendMessage(uid1, clientId1, sessionId1.getValue(), peerIdCaptor1.getValue(), null, + messageIdBase1 + i, 0); + } + for (int i = 0; i < numberOfMessages; ++i) { + mDut.sendMessage(uid2, clientId2, sessionId2.getValue(), peerIdCaptor2.getValue(), null, + messageIdBase2 + i, 0); + } + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback, times(numOfReject * 2)) + .onMessageSendFail(messageIdCaptorFail.capture(), + eq(NanStatusType.INTERNAL_FAILURE)); + + // (4) Transmit messages + int successNum = 0; + for (int i = 0; i < numberOfMessages * 2; ++i) { + if (answerObj.process()) { + successNum++; + } else { + break; + } + mMockLooper.dispatchAll(); + } + assertEquals("queue empty", 0, answerObj.queueSize()); + assertEquals("success message num", messageQueueDepthPerUid * 2, successNum); + inOrder.verify(mockSessionCallback, times(messageQueueDepthPerUid * 2)) + .onMessageSendSuccess(messageIdCaptorSuccess.capture()); + + for (int i = 0; i < numOfReject; ++i) { + assertEquals("message ID: " + i + messageQueueDepthPerUid, + messageIdBase1 + i + messageQueueDepthPerUid, + (int) messageIdCaptorFail.getAllValues().get(i)); + assertEquals("message ID: " + i + messageQueueDepthPerUid, + messageIdBase2 + i + messageQueueDepthPerUid, + (int) messageIdCaptorFail.getAllValues().get(i + numOfReject)); + } + + for (int i = 0; i < messageQueueDepthPerUid; ++i) { + assertEquals("message ID: " + i, messageIdBase1 + i, + (int) messageIdCaptorSuccess.getAllValues().get(i)); + assertEquals("message ID: " + i, messageIdBase2 + i, + (int) messageIdCaptorSuccess.getAllValues().get(i + messageQueueDepthPerUid)); + } + + 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 @@ -2250,7 +2399,7 @@ public class WifiAwareStateManagerTest { any(), anyInt())).thenAnswer(answerObj); for (int i = 0; i < numberOfMessages; ++i) { - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), null, + mDut.sendMessage(uid + i, clientId, sessionId.getValue(), peerIdCaptor.getValue(), null, messageIdBase + i, retransmitCount); mMockLooper.dispatchAll(); } @@ -2334,8 +2483,8 @@ public class WifiAwareStateManagerTest { eq(peerMatchFilter.getBytes())); // (3) message null Tx successful queuing - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), null, messageId, - 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + null, messageId, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), isNull(byte[].class), eq(messageId)); @@ -2350,7 +2499,7 @@ public class WifiAwareStateManagerTest { validateInternalSendMessageQueuesCleanedUp(messageId); // (5) message byte[0] Tx successful queuing - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), new byte[0], + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), new byte[0], messageId, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), @@ -2366,8 +2515,8 @@ public class WifiAwareStateManagerTest { validateInternalSendMessageQueuesCleanedUp(messageId); // (7) message "" Tx successful queuing - mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), "".getBytes(), - messageId, 0); + mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), + "".getBytes(), messageId, 0); mMockLooper.dispatchAll(); inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), eq(requestorId), eq(peerMac), byteArrayCaptor.capture(), eq(messageId)); @@ -3126,6 +3275,7 @@ public class WifiAwareStateManagerTest { inOrder.verify(mMockNativeManager).start(any(Handler.class)); mDut.enableUsage(); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); mMockLooper.dispatchAll(); inOrder.verify(mMockNativeManager).tryToGetAware(); inOrder.verify(mMockNative).getCapabilities(transactionId.capture()); @@ -3165,8 +3315,16 @@ public class WifiAwareStateManagerTest { simulateWifiStateChange(true); mMockLooper.dispatchAll(); + // when WifiAware Native is not available, DOZE OFF -> no change + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(false); + simulatePowerStateChangeDoze(false); + mMockLooper.dispatchAll(); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true); + // (5) power state change: DOZE OFF simulatePowerStateChangeDoze(false); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); mMockLooper.dispatchAll(); collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true)); validateCorrectAwareStatusChangeBroadcast(inOrder); @@ -3192,6 +3350,7 @@ public class WifiAwareStateManagerTest { inOrder.verify(mMockNativeManager).start(any(Handler.class)); mDut.enableUsage(); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); mMockLooper.dispatchAll(); inOrder.verify(mMockNativeManager).tryToGetAware(); inOrder.verify(mMockNative).getCapabilities(transactionId.capture()); @@ -3227,8 +3386,16 @@ public class WifiAwareStateManagerTest { simulateWifiStateChange(true); mMockLooper.dispatchAll(); + // when WifiAware Native is not available, enable location -> no change + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(false); + simulateLocationModeChange(true); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); + mMockLooper.dispatchAll(); + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true); + // (4) location mode change: enable simulateLocationModeChange(true); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); mMockLooper.dispatchAll(); collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true)); validateCorrectAwareStatusChangeBroadcast(inOrder); @@ -3254,6 +3421,7 @@ public class WifiAwareStateManagerTest { inOrder.verify(mMockNativeManager).start(any(Handler.class)); mDut.enableUsage(); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); mMockLooper.dispatchAll(); inOrder.verify(mMockNativeManager).tryToGetAware(); inOrder.verify(mMockNative).getCapabilities(transactionId.capture()); @@ -3289,8 +3457,16 @@ public class WifiAwareStateManagerTest { simulateLocationModeChange(true); mMockLooper.dispatchAll(); + // when WifiAware Native is not available, enable Wifi -> no change + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(false); + simulateWifiStateChange(true); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); + mMockLooper.dispatchAll(); + when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true); + // (4) wifi state change: enable simulateWifiStateChange(true); + inOrder.verify(mMockNativeManager).isAwareNativeAvailable(); mMockLooper.dispatchAll(); collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true)); validateCorrectAwareStatusChangeBroadcast(inOrder); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPDataTest.java index addb9b5b5b..a467f822f8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPDataTest.java @@ -24,6 +24,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.anqp.ANQPElement; import com.android.server.wifi.hotspot2.anqp.Constants; @@ -40,7 +41,7 @@ import java.util.Map; * allow easy construction of ANQP elements for testing. */ @SmallTest -public class ANQPDataTest { +public class ANQPDataTest extends WifiBaseTest { @Mock Clock mClock; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java index 4d4ea44873..5e2f3e1efb 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java @@ -25,6 +25,7 @@ import android.net.wifi.EAPConstants; import androidx.test.filters.SmallTest; import com.android.server.wifi.IMSIParameter; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.anqp.CellularNetwork; import com.android.server.wifi.hotspot2.anqp.DomainNameElement; import com.android.server.wifi.hotspot2.anqp.NAIRealmData; @@ -50,7 +51,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPMatcher}. */ @SmallTest -public class ANQPMatcherTest { +public class ANQPMatcherTest extends WifiBaseTest { private static final String TEST_MCC_MNC = "123456"; private static final String TEST_3GPP_FQDN = String.format("wlan.mnc%s.mcc%s.3gppnetwork.org", TEST_MCC_MNC.substring(3), TEST_MCC_MNC.substring(0, 3)); @@ -458,4 +459,19 @@ public class ANQPMatcherTest { assertEquals(-1, ANQPMatcher.getCarrierEapMethodFromMatchingNAIRealm(TEST_3GPP_FQDN, element)); } + + /** + * Verify that domain name match will fail when domain contains invalid values. + * + * @throws Exception + */ + @Test + public void verifyInvalidDomain() 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.mccI23.3gppnetwork.org"}; + DomainNameElement element = new DomainNameElement(Arrays.asList(domains)); + assertFalse(ANQPMatcher.matchDomainName(element, null, imsiParam, simImsiList)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPNetworkKeyTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPNetworkKeyTest.java index 74a884d7be..fb74940458 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPNetworkKeyTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPNetworkKeyTest.java @@ -20,13 +20,15 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; /** * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPNetworkKey}. */ @SmallTest -public class ANQPNetworkKeyTest { +public class ANQPNetworkKeyTest extends WifiBaseTest { private static final String SSID = "TestSSID"; private static final long BSSID = 0x123456L; private static final long HESSID = 0x789012L; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java index b252f817e0..bc9d02d31f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java @@ -30,6 +30,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.anqp.Constants; import org.junit.Before; @@ -43,7 +44,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPRequestManager}. */ @SmallTest -public class ANQPRequestManagerTest { +public class ANQPRequestManagerTest extends WifiBaseTest { private static final long TEST_BSSID = 0x123456L; private static final ANQPNetworkKey TEST_ANQP_KEY = new ANQPNetworkKey("TestSSID", TEST_BSSID, 0, 0); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/AnqpCacheTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/AnqpCacheTest.java index 3084e064a2..1e3fdae393 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/AnqpCacheTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/AnqpCacheTest.java @@ -25,6 +25,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.ANQPData; import com.android.server.wifi.hotspot2.AnqpCache; @@ -39,7 +40,7 @@ import org.mockito.Mock; * allow easy construction of ANQP elements for testing. */ @SmallTest -public class AnqpCacheTest { +public class AnqpCacheTest extends WifiBaseTest { private static final ANQPNetworkKey ENTRY_KEY = new ANQPNetworkKey("test", 0L, 0L, 1); @Mock Clock mClock; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/DomainMatcherTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/DomainMatcherTest.java index 1671507bf4..d0e41d250d 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/DomainMatcherTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/DomainMatcherTest.java @@ -24,6 +24,8 @@ import android.util.Pair; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.util.Arrays; @@ -34,7 +36,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.hotspot2.DomainMatcher}. */ @SmallTest -public class DomainMatcherTest { +public class DomainMatcherTest extends WifiBaseTest { 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"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuNetworkConnectionTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuNetworkConnectionTest.java index acfb4b47e7..8f3daa2432 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuNetworkConnectionTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuNetworkConnectionTest.java @@ -39,6 +39,7 @@ import android.net.NetworkRequest; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; @@ -48,6 +49,7 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; import com.android.server.wifi.TestUtil; +import com.android.server.wifi.WifiBaseTest; import org.junit.Before; import org.junit.Test; @@ -61,7 +63,7 @@ import java.net.InetAddress; * Unit tests for {@link OsuNetworkConnection}. */ @SmallTest -public class OsuNetworkConnectionTest { +public class OsuNetworkConnectionTest extends WifiBaseTest { private static final String TAG = "OsuNetworkConnectionTest"; private static final int ENABLE_LOGGING = 1; private static final int DISABLE_LOGGING = 0; @@ -169,16 +171,6 @@ public class OsuNetworkConnectionTest { } /** - * Verifies that connect() API returns false when OSU AP is a part of an OSEN - */ - @Test - public void verifyOSENUnsupported() { - mNetworkConnection.init(mHandler); - assertEquals(false, - mNetworkConnection.connect(TEST_SSID, TEST_NAI_OSEN, TEST_PROVIDER_NAME)); - } - - /** * Verifies that connect() API returns false when WifiManager's addNetwork() * returns an invalid network ID */ @@ -327,4 +319,37 @@ public class OsuNetworkConnectionTest { assertFalse(networkRequestCaptor.getValue().hasCapability(NET_CAPABILITY_TRUSTED)); } + + /** + * Verifies that {@link WifiConfiguration} has been created properly for OSEN OSU network. + * It is supposed to create a network as ephemeral network with OSEN protocol and key management + * and suppress no internet access notification. + */ + @Test + public void verifyWifiConfigurationForOsenOsuNetwork() { + mNetworkConnection.init(mHandler); + + assertEquals(true, mNetworkConnection.connect(TEST_SSID, TEST_NAI_OSEN, + TEST_PROVIDER_NAME)); + + ArgumentCaptor<WifiConfiguration> wifiConfigurationCaptor = ArgumentCaptor.forClass( + WifiConfiguration.class); + verify(mWifiManager, times(1)).addNetwork(wifiConfigurationCaptor.capture()); + WifiConfiguration wifiConfiguration = wifiConfigurationCaptor.getValue(); + assertTrue(wifiConfiguration.isNoInternetAccessExpected()); + assertTrue(wifiConfiguration.isEphemeral()); + assertTrue(wifiConfiguration.osu); + assertTrue(wifiConfiguration.allowedProtocols.get(WifiConfiguration.Protocol.OSEN)); + assertTrue(wifiConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OSEN)); + assertEquals(wifiConfiguration.enterpriseConfig.getEapMethod(), + WifiEnterpriseConfig.Eap.UNAUTH_TLS); + assertEquals(wifiConfiguration.enterpriseConfig.getCaPath(), + WfaKeyStore.DEFAULT_WFA_CERT_DIR); + ArgumentCaptor<NetworkRequest> networkRequestCaptor = ArgumentCaptor.forClass( + NetworkRequest.class); + verify(mConnectivityManager, times(1)).requestNetwork(networkRequestCaptor.capture(), + any(ConnectivityManager.NetworkCallback.class), any(Handler.class), anyInt()); + assertFalse(networkRequestCaptor.getValue().hasCapability(NET_CAPABILITY_TRUSTED)); + + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java index 9fa92c9a30..fbb653bc01 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuServerConnectionTest.java @@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.org.conscrypt.TrustManagerImpl; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.soap.HttpsServiceConnection; import com.android.server.wifi.hotspot2.soap.HttpsTransport; import com.android.server.wifi.hotspot2.soap.SoapParser; @@ -80,7 +81,7 @@ import javax.net.ssl.X509TrustManager; * Unit tests for {@link OsuServerConnection}. */ @SmallTest -public class OsuServerConnectionTest { +public class OsuServerConnectionTest extends WifiBaseTest { private static final String TEST_VALID_URL = "https://www.google.com"; private static final String TEST_INVALID_URL = "http://www.google.com"; private static final String AUTH_TYPE = "ECDHE_RSA"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java index c76e2c878c..95251a70d8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreDataTest.java @@ -24,6 +24,7 @@ import android.util.Xml; import androidx.test.filters.SmallTest; import com.android.internal.util.FastXmlSerializer; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiConfigStore; import org.junit.Before; @@ -41,7 +42,7 @@ import java.nio.charset.StandardCharsets; * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointConfigSharedStoreData}. */ @SmallTest -public class PasspointConfigSharedStoreDataTest { +public class PasspointConfigSharedStoreDataTest extends WifiBaseTest { @Mock PasspointConfigSharedStoreData.DataSource mDataSource; PasspointConfigSharedStoreData mConfigStoreData; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java index 82cdb5a905..54a74978db 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigUserStoreDataTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.FastXmlSerializer; import com.android.server.wifi.SIMAccessor; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiConfigStore; import com.android.server.wifi.WifiKeyStore; @@ -56,7 +57,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointConfigUserStoreData}. */ @SmallTest -public class PasspointConfigUserStoreDataTest { +public class PasspointConfigUserStoreDataTest extends WifiBaseTest { private static final String TEST_CA_CERTIFICATE_ALIAS = "CaCert"; private static final String TEST_CA_CERTIFICATE_ALIAS_2 = "CaCert_2"; private static final String TEST_CLIENT_CERTIFICATE_ALIAS = "ClientCert"; @@ -243,11 +244,11 @@ public class PasspointConfigUserStoreDataTest { List<PasspointProvider> providerList = new ArrayList<>(); providerList.add(new PasspointProvider(createFullPasspointConfiguration(), mKeyStore, mSimAccessor, TEST_PROVIDER_ID, TEST_CREATOR_UID, TEST_CREATOR_PACKAGE, - Arrays.asList(TEST_CA_CERTIFICATE_ALIAS), TEST_CLIENT_CERTIFICATE_ALIAS, + false, Arrays.asList(TEST_CA_CERTIFICATE_ALIAS), TEST_CLIENT_CERTIFICATE_ALIAS, TEST_CLIENT_PRIVATE_KEY_ALIAS, null, TEST_HAS_EVER_CONNECTED, TEST_SHARED)); providerList.add(new PasspointProvider(createFullPasspointConfiguration(), mKeyStore, mSimAccessor, TEST_PROVIDER_ID_2, TEST_CREATOR_UID, TEST_CREATOR_PACKAGE, - Arrays.asList(TEST_CA_CERTIFICATE_ALIAS, TEST_CA_CERTIFICATE_ALIAS_2), + true, Arrays.asList(TEST_CA_CERTIFICATE_ALIAS, TEST_CA_CERTIFICATE_ALIAS_2), TEST_CLIENT_CERTIFICATE_ALIAS, TEST_CLIENT_PRIVATE_KEY_ALIAS, TEST_REMEDIATION_CA_CERTIFICATE_ALIAS, TEST_HAS_EVER_CONNECTED, TEST_SHARED)); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointEventHandlerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointEventHandlerTest.java index d958f5a8c8..c21697e696 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointEventHandlerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointEventHandlerTest.java @@ -26,6 +26,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiNative; import com.android.server.wifi.hotspot2.anqp.Constants; @@ -42,7 +43,7 @@ import java.util.List; * TODO(zqiu): add more test when switch over to use wificond. */ @SmallTest -public class PasspointEventHandlerTest { +public class PasspointEventHandlerTest extends WifiBaseTest { private static final String TAG = "PasspointEventHandlerTest"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java index 131425af88..41cf336512 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java @@ -48,6 +48,7 @@ import static org.mockito.Mockito.lenient; 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 static org.mockito.MockitoAnnotations.initMocks; @@ -72,6 +73,7 @@ import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.os.test.TestLooper; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Base64; @@ -86,6 +88,7 @@ import com.android.server.wifi.FakeKeys; import com.android.server.wifi.IMSIParameter; import com.android.server.wifi.SIMAccessor; import com.android.server.wifi.ScanDetail; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiConfigManager; import com.android.server.wifi.WifiConfigStore; import com.android.server.wifi.WifiConfigurationTestUtil; @@ -93,6 +96,7 @@ import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiKeyStore; import com.android.server.wifi.WifiMetrics; import com.android.server.wifi.WifiNative; +import com.android.server.wifi.WifiNetworkSuggestionsManager; 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; @@ -117,6 +121,7 @@ import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -128,9 +133,10 @@ import java.util.Set; * Unit tests for {@link PasspointManager}. */ @SmallTest -public class PasspointManagerTest { +public class PasspointManagerTest extends WifiBaseTest { private static final long BSSID = 0x112233445566L; private static final String TEST_PACKAGE = "com.android.test"; + private static final String TEST_PACKAGE1 = "com.android.test1"; private static final String ICON_FILENAME = "test"; private static final String TEST_FQDN = "test1.test.com"; private static final String TEST_FQDN2 = "test2.test.com"; @@ -159,6 +165,7 @@ public class PasspointManagerTest { private static final ANQPNetworkKey TEST_ANQP_KEY2 = ANQPNetworkKey.buildKey( TEST_SSID, TEST_BSSID, TEST_HESSID, TEST_ANQP_DOMAIN_ID2); private static final int TEST_CREATOR_UID = 1234; + private static final int TEST_CREATOR_UID1 = 1235; private static final int TEST_UID = 1500; @Mock Context mContext; @@ -188,6 +195,7 @@ public class PasspointManagerTest { @Mock TelephonyManager mTelephonyManager; @Mock TelephonyManager mDataTelephonyManager; @Mock SubscriptionManager mSubscriptionManager; + @Mock WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; Handler mHandler; TestLooper mLooper; @@ -214,6 +222,8 @@ public class PasspointManagerTest { .thenReturn(mPasspointProvisioner); when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mWifiInjector.getClientModeImpl()).thenReturn(mClientModeImpl); + when(mWifiInjector.getWifiNetworkSuggestionsManager()) + .thenReturn(mWifiNetworkSuggestionsManager); mLooper = new TestLooper(); mHandler = new Handler(mLooper.getLooper()); mManager = new PasspointManager(mContext, mWifiInjector, mHandler, mWifiNative, @@ -234,7 +244,8 @@ public class PasspointManagerTest { mSharedDataSource = sharedDataSource.getValue(); mUserDataSource = userDataSource.getValue(); // SIM is absent - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[0]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Collections.emptyList()); } /** @@ -274,16 +285,24 @@ public class PasspointManagerTest { assertEquals(expectedConfig, installedConfigs.get(0)); } + private PasspointProvider createMockProvider(PasspointConfiguration config) { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.FQDN = config.getHomeSp().getFqdn(); + return createMockProvider(config, wifiConfig); + } + /** * 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) { + private PasspointProvider createMockProvider( + PasspointConfiguration config, WifiConfiguration wifiConfig) { PasspointProvider provider = mock(PasspointProvider.class); when(provider.installCertsAndKeys()).thenReturn(true); lenient().when(provider.getConfig()).thenReturn(config); + lenient().when(provider.getWifiConfig()).thenReturn(wifiConfig); lenient().when(provider.getCreatorUid()).thenReturn(TEST_CREATOR_UID); return provider; } @@ -340,6 +359,13 @@ public class PasspointManagerTest { return config; } + private PasspointProvider addTestProvider(String fqdn, String friendlyName, + String packageName) { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.FQDN = fqdn; + return addTestProvider(fqdn, friendlyName, packageName, wifiConfig); + } + /** * Helper function for adding a test provider to the manager. Return the mock * provider that's added to the manager. @@ -347,14 +373,14 @@ public class PasspointManagerTest { * @return {@link PasspointProvider} */ private PasspointProvider addTestProvider(String fqdn, String friendlyName, - String packageName) { + String packageName, WifiConfiguration wifiConfig) { PasspointConfiguration config = createTestConfigWithUserCredential(fqdn, friendlyName); - PasspointProvider provider = createMockProvider(config); + PasspointProvider provider = createMockProvider(config, wifiConfig); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - provider); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(provider); when(provider.getPackageName()).thenReturn(packageName); - assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); return provider; } @@ -368,10 +394,10 @@ public class PasspointManagerTest { PasspointConfiguration config = createTestConfigWithSimCredential(fqdn, imsi, realm); PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - provider); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(provider); - assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); return provider; } @@ -587,7 +613,7 @@ public class PasspointManagerTest { */ @Test public void addProviderWithNullConfig() throws Exception { - assertFalse(mManager.addOrUpdateProvider(null, TEST_CREATOR_UID, TEST_PACKAGE)); + assertFalse(mManager.addOrUpdateProvider(null, TEST_CREATOR_UID, TEST_PACKAGE, false)); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess(); } @@ -600,7 +626,7 @@ public class PasspointManagerTest { @Test public void addProviderWithEmptyConfig() throws Exception { assertFalse(mManager.addOrUpdateProvider(new PasspointConfiguration(), TEST_CREATOR_UID, - TEST_PACKAGE)); + TEST_PACKAGE, false)); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess(); } @@ -617,32 +643,32 @@ public class PasspointManagerTest { TEST_FRIENDLY_NAME); // EAP-TLS not allowed for user credential. config.getCredential().getUserCredential().setEapType(EAPConstants.EAP_TLS); - assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess(); } /** - * Verify that adding a provider with a valid configuration and user credential will succeed. + * Verify that adding a user saved provider with a valid configuration and user credential will + * succeed. * * @throws Exception */ @Test - public void addRemoveProviderWithValidUserCredential() throws Exception { + public void addRemoveSavedProviderWithValidUserCredential() throws Exception { PasspointConfiguration config = createTestConfigWithUserCredential(TEST_FQDN, TEST_FRIENDLY_NAME); PasspointProvider provider = createMockProvider(config); when(provider.getPackageName()).thenReturn(TEST_PACKAGE); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - provider); - assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(provider); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verifyInstalledConfig(config); - verify(mWifiConfigManager).saveToStore(true); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); - verify(mAppOpsManager).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE), eq(TEST_PACKAGE), any( - AppOpsManager.OnOpChangedListener.class)); + verify(mAppOpsManager).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE), eq(TEST_PACKAGE), + any(AppOpsManager.OnOpChangedListener.class)); reset(mWifiMetrics); reset(mWifiConfigManager); @@ -656,6 +682,8 @@ public class PasspointManagerTest { // Remove the provider as the creator app. assertTrue(mManager.removeProvider(TEST_CREATOR_UID, false, TEST_FQDN)); verify(provider).uninstallCertsAndKeys(); + verify(mWifiConfigManager).removePasspointConfiguredNetwork( + provider.getWifiConfig().configKey()); verify(mWifiConfigManager).saveToStore(true); verify(mWifiMetrics).incrementNumPasspointProviderUninstallation(); verify(mWifiMetrics).incrementNumPasspointProviderUninstallSuccess(); @@ -669,19 +697,20 @@ public class PasspointManagerTest { } /** - * Verify that adding a provider with a valid configuration and SIM credential will succeed. + * Verify that adding a user saved provider with a valid configuration and SIM credential will + * succeed. * * @throws Exception */ @Test - public void addRemoveProviderWithValidSimCredential() throws Exception { + public void addRemoveSavedProviderWithValidSimCredential() throws Exception { PasspointConfiguration config = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, TEST_REALM); PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - provider); - assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(provider); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verifyInstalledConfig(config); verify(mWifiConfigManager).saveToStore(true); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); @@ -699,6 +728,8 @@ public class PasspointManagerTest { // Remove the provider as a privileged non-creator app. assertTrue(mManager.removeProvider(TEST_UID, true, TEST_FQDN)); verify(provider).uninstallCertsAndKeys(); + verify(mWifiConfigManager).removePasspointConfiguredNetwork( + provider.getWifiConfig().configKey()); verify(mWifiConfigManager).saveToStore(true); verify(mWifiMetrics).incrementNumPasspointProviderUninstallation(); verify(mWifiMetrics).incrementNumPasspointProviderUninstallSuccess(); @@ -711,22 +742,22 @@ public class PasspointManagerTest { } /** - * 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 + * Verify that adding a user saved 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 { + public void addSavedProviderWithExistingConfig() throws Exception { // Add a provider with the original configuration. PasspointConfiguration origConfig = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, TEST_REALM); PasspointProvider origProvider = createMockProvider(origConfig); when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - origProvider); - assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID, TEST_PACKAGE)); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(origProvider); + assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID, TEST_PACKAGE, false)); verifyInstalledConfig(origConfig); verify(mWifiConfigManager).saveToStore(true); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); @@ -746,10 +777,12 @@ public class PasspointManagerTest { TEST_FRIENDLY_NAME); PasspointProvider newProvider = createMockProvider(newConfig); when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - newProvider); - assertTrue(mManager.addOrUpdateProvider(newConfig, TEST_CREATOR_UID, TEST_PACKAGE)); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(newProvider); + assertTrue(mManager.addOrUpdateProvider(newConfig, TEST_CREATOR_UID, TEST_PACKAGE, false)); verifyInstalledConfig(newConfig); + verify(mWifiConfigManager).removePasspointConfiguredNetwork( + newProvider.getWifiConfig().configKey()); verify(mWifiConfigManager).saveToStore(true); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); @@ -774,9 +807,9 @@ public class PasspointManagerTest { PasspointProvider provider = mock(PasspointProvider.class); when(provider.installCertsAndKeys()).thenReturn(false); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - provider); - assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), eq(false))) + .thenReturn(provider); + assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess(); } @@ -792,7 +825,7 @@ public class PasspointManagerTest { TEST_FRIENDLY_NAME); doThrow(new GeneralSecurityException()) .when(mCertVerifier).verifyCaCert(any(X509Certificate.class)); - assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess(); } @@ -810,9 +843,9 @@ public class PasspointManagerTest { config.setUpdateIdentifier(1); PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - provider); - assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(provider); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verify(mCertVerifier, never()).verifyCaCert(any(X509Certificate.class)); verifyInstalledConfig(config); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); @@ -963,15 +996,11 @@ public class PasspointManagerTest { try { PasspointProvider providerHome = addTestProvider(TEST_FQDN + 0, TEST_FRIENDLY_NAME, TEST_PACKAGE); - WifiConfiguration homeWifiConfiguration = new WifiConfiguration(); - homeWifiConfiguration.FQDN = TEST_FQDN + 0; - homeWifiConfiguration.isHomeProviderNetwork = true; + providerHome.getWifiConfig().isHomeProviderNetwork = true; PasspointProvider providerRoaming = addTestProvider(TEST_FQDN + 1, TEST_FRIENDLY_NAME, TEST_PACKAGE); - WifiConfiguration roamingWifiConfiguration = new WifiConfiguration(); - roamingWifiConfiguration.FQDN = TEST_FQDN + 1; PasspointProvider providerNone = addTestProvider(TEST_FQDN + 2, TEST_FRIENDLY_NAME, - TEST_PACKAGE); + TEST_PACKAGE, new WifiConfiguration()); ANQPData entry = new ANQPData(mClock, null); InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa(); vsa.anqpDomainID = TEST_ANQP_DOMAIN_ID2; @@ -985,10 +1014,6 @@ public class PasspointManagerTest { when(providerNone.match(anyMap(), isNull())) .thenReturn(PasspointMatch.None); - lenient().when(providerHome.getWifiConfig()).thenReturn(homeWifiConfiguration); - lenient().when(providerRoaming.getWifiConfig()).thenReturn(roamingWifiConfiguration); - lenient().when(providerNone.getWifiConfig()).thenReturn(new WifiConfiguration()); - Map<String, Map<Integer, List<ScanResult>>> configs = mManager.getAllMatchingFqdnsForScanResults( createTestScanResults()); @@ -1016,19 +1041,10 @@ public class PasspointManagerTest { @Test public void getWifiConfigsForPasspointProfiles() { PasspointProvider provider1 = addTestProvider(TEST_FQDN, TEST_FRIENDLY_NAME, TEST_PACKAGE); - WifiConfiguration wifiConfiguration1 = new WifiConfiguration(); - wifiConfiguration1.FQDN = TEST_FQDN; PasspointProvider provider2 = addTestProvider(TEST_FQDN + 1, TEST_FRIENDLY_NAME, TEST_PACKAGE); - WifiConfiguration wifiConfiguration2 = new WifiConfiguration(); - wifiConfiguration2.FQDN = TEST_FQDN + 1; PasspointProvider provider3 = addTestProvider(TEST_FQDN + 2, TEST_FRIENDLY_NAME, TEST_PACKAGE); - WifiConfiguration wifiConfiguration3 = new WifiConfiguration(); - wifiConfiguration3.FQDN = TEST_FQDN + 2; - lenient().when(provider1.getWifiConfig()).thenReturn(wifiConfiguration1); - lenient().when(provider2.getWifiConfig()).thenReturn(wifiConfiguration2); - lenient().when(provider3.getWifiConfig()).thenReturn(wifiConfiguration3); assertEquals(3, mManager.getWifiConfigsForPasspointProfiles( Arrays.asList(TEST_FQDN, TEST_FQDN + 1, TEST_FQDN + 2)).size()); @@ -1310,11 +1326,10 @@ public class PasspointManagerTest { // 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), - eq(TEST_PACKAGE))).thenReturn(provider); + eq(TEST_PACKAGE), eq(false))).thenReturn(provider); - assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verifyInstalledConfig(config); - verify(mWifiConfigManager).saveToStore(true); reset(mWifiConfigManager); } @@ -1669,11 +1684,12 @@ public class PasspointManagerTest { @Test public void verifyInstallEphemeralPasspointConfigurationWithNonCarrierEapMethod() { // SIM is present - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[1]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(mock(SubscriptionInfo.class))); PasspointConfiguration config = createTestConfigWithUserCredential("abc.com", "test"); PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), anyInt(), isNull())).thenReturn(provider); + eq(mSimAccessor), anyLong(), anyInt(), isNull(), eq(false))).thenReturn(provider); assertFalse(mManager.installEphemeralPasspointConfigForCarrier(config)); } @@ -1684,12 +1700,13 @@ public class PasspointManagerTest { @Test public void verifyInstallEphemeralPasspointConfiguration() { // SIM is present - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[1]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(mock(SubscriptionInfo.class))); PasspointConfiguration config = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, TEST_REALM); PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), anyInt(), isNull())).thenReturn(provider); + eq(mSimAccessor), anyLong(), anyInt(), isNull(), eq(false))).thenReturn(provider); assertTrue(mManager.installEphemeralPasspointConfigForCarrier(config)); verify(mAppOpsManager, never()).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE), @@ -1730,7 +1747,8 @@ public class PasspointManagerTest { .thenReturn(mDataTelephonyManager); when(mDataTelephonyManager.getSimOperator()).thenReturn(TEST_MCC_MNC); // SIM is present - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[1]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(mock(SubscriptionInfo.class))); List<ScanDetail> scanDetails = new ArrayList<>(); scanDetails.add(generateScanDetail(TEST_SSID, TEST_BSSID_STRING, TEST_HESSID, TEST_ANQP_DOMAIN_ID, true)); @@ -1774,7 +1792,8 @@ public class PasspointManagerTest { .thenReturn(mDataTelephonyManager); when(mDataTelephonyManager.getSimOperator()).thenReturn(TEST_MCC_MNC); // SIM is present - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[1]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(mock(SubscriptionInfo.class))); List<ScanDetail> scanDetails = new ArrayList<>(); scanDetails.add(generateScanDetail(TEST_SSID, TEST_BSSID_STRING, 0, 0, false)); @@ -1830,9 +1849,9 @@ public class PasspointManagerTest { TEST_REALM); PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), - eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE))).thenReturn( - provider); - assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE)); + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(provider); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, false)); verifyInstalledConfig(config); verify(mWifiConfigManager).saveToStore(true); verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); @@ -1857,4 +1876,440 @@ public class PasspointManagerTest { // 1 profile available for TEST_CREATOR_UID assertFalse(mManager.getProviderConfigs(TEST_CREATOR_UID, false).isEmpty()); } + + /** + * Verify that adding a suggestion provider with a valid configuration and user credential will + * succeed. + * + * @throws Exception + */ + @Test + public void addRemoveSuggestionProvider() throws Exception { + PasspointConfiguration config = createTestConfigWithUserCredential(TEST_FQDN, + TEST_FRIENDLY_NAME); + PasspointProvider provider = createMockProvider(config); + when(provider.getPackageName()).thenReturn(TEST_PACKAGE); + when(provider.isFromSuggestion()).thenReturn(true); + when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(true))).thenReturn(provider); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID, TEST_PACKAGE, true)); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + verify(mAppOpsManager, never()).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE), + eq(TEST_PACKAGE), any(AppOpsManager.OnOpChangedListener.class)); + assertTrue(mManager.getProviderConfigs(TEST_CREATOR_UID, false).isEmpty()); + reset(mWifiMetrics); + reset(mWifiConfigManager); + + // Verify content in the data source. + List<PasspointProvider> providers = mUserDataSource.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, mSharedDataSource.getProviderIndex()); + + // Remove from another Suggestor app, should fail. + assertFalse(mManager.removeProvider(TEST_UID, false, TEST_FQDN)); + verify(provider, never()).uninstallCertsAndKeys(); + verify(mWifiConfigManager, never()).removePasspointConfiguredNetwork( + provider.getWifiConfig().configKey()); + verify(mWifiConfigManager, never()).saveToStore(true); + verify(mWifiMetrics).incrementNumPasspointProviderUninstallation(); + verify(mWifiMetrics, never()).incrementNumPasspointProviderUninstallSuccess(); + verify(mAppOpsManager, never()).stopWatchingMode( + any(AppOpsManager.OnOpChangedListener.class)); + // Verify content in the data source. + providers = mUserDataSource.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, mSharedDataSource.getProviderIndex()); + reset(mWifiMetrics); + reset(mWifiConfigManager); + + // Remove the provider from same app. + assertTrue(mManager.removeProvider(TEST_CREATOR_UID, false, TEST_FQDN)); + verify(provider).uninstallCertsAndKeys(); + verify(mWifiConfigManager).removePasspointConfiguredNetwork( + provider.getWifiConfig().configKey()); + verify(mWifiConfigManager).saveToStore(true); + verify(mWifiMetrics).incrementNumPasspointProviderUninstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderUninstallSuccess(); + verify(mAppOpsManager, never()).stopWatchingMode( + any(AppOpsManager.OnOpChangedListener.class)); + + // Verify content in the data source. + assertTrue(mUserDataSource.getProviders().isEmpty()); + // Removing a provider should not change the provider index. + assertEquals(1, mSharedDataSource.getProviderIndex()); + } + + /** + * Verify that adding a suggestion provider with the same base domain as the existing + * suggestion provider from same app will succeed, and verify that the existing provider is + * replaced by the new provider with the new configuration. + * + * @throws Exception + */ + @Test + public void addSuggestionProviderWithExistingConfig() throws Exception { + // Add a provider with the original configuration. + PasspointConfiguration origConfig = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, + TEST_REALM); + PasspointProvider origProvider = createMockProvider(origConfig); + when(origProvider.getPackageName()).thenReturn(TEST_PACKAGE); + when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(true))).thenReturn(origProvider); + assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID, TEST_PACKAGE, true)); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + reset(mWifiMetrics); + reset(mWifiConfigManager); + + // Verify data source content. + List<PasspointProvider> origProviders = mUserDataSource.getProviders(); + assertEquals(1, origProviders.size()); + assertEquals(origConfig, origProviders.get(0).getConfig()); + assertEquals(1, mSharedDataSource.getProviderIndex()); + + // Add same provider as existing suggestion provider + // This should be no WifiConfig deletion + assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID, TEST_PACKAGE, true)); + verify(mWifiConfigManager, never()).removePasspointConfiguredNetwork( + origProvider.getWifiConfig().configKey()); + verify(mWifiConfigManager).saveToStore(true); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + assertEquals(2, mSharedDataSource.getProviderIndex()); + reset(mWifiMetrics); + reset(mWifiConfigManager); + + // Add another provider with the same base domain as the existing saved provider. + // This should replace the existing provider with the new configuration. + PasspointConfiguration newConfig = createTestConfigWithUserCredential(TEST_FQDN, + TEST_FRIENDLY_NAME); + PasspointProvider newProvider = createMockProvider(newConfig); + when(newProvider.isFromSuggestion()).thenReturn(true); + when(newProvider.getPackageName()).thenReturn(TEST_PACKAGE); + when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(true))).thenReturn(newProvider); + assertTrue(mManager.addOrUpdateProvider(newConfig, TEST_CREATOR_UID, TEST_PACKAGE, true)); + verify(mWifiConfigManager).removePasspointConfiguredNetwork( + newProvider.getWifiConfig().configKey()); + verify(mWifiConfigManager).saveToStore(true); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + + // Verify data source content. + List<PasspointProvider> newProviders = mUserDataSource.getProviders(); + assertEquals(1, newProviders.size()); + assertEquals(newConfig, newProviders.get(0).getConfig()); + assertEquals(3, mSharedDataSource.getProviderIndex()); + } + + /** + * Verify that adding a saved provider with the same base domain as the existing + * suggestion provider will succeed, and verify that the existing provider is + * replaced by the new provider with the new configuration. + * + * @throws Exception + */ + @Test + public void addSavedProviderWithExistingSuggestionConfig() throws Exception { + // Add a provider with the original configuration. + PasspointConfiguration origConfig = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, + TEST_REALM); + PasspointProvider origProvider = createMockProvider(origConfig); + when(origProvider.getPackageName()).thenReturn(TEST_PACKAGE); + when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(true))).thenReturn(origProvider); + assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID, TEST_PACKAGE, true)); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + reset(mWifiMetrics); + reset(mWifiConfigManager); + + // Verify data source content. + List<PasspointProvider> origProviders = mUserDataSource.getProviders(); + assertEquals(1, origProviders.size()); + assertEquals(origConfig, origProviders.get(0).getConfig()); + assertEquals(1, mSharedDataSource.getProviderIndex()); + + // Add another provider with the same base domain as the existing saved provider. + // This should replace the existing provider with the new configuration. + PasspointConfiguration newConfig = createTestConfigWithUserCredential(TEST_FQDN, + TEST_FRIENDLY_NAME); + PasspointProvider newProvider = createMockProvider(newConfig); + when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(newProvider); + assertTrue(mManager.addOrUpdateProvider(newConfig, TEST_CREATOR_UID, TEST_PACKAGE, false)); + verify(mWifiConfigManager).removePasspointConfiguredNetwork( + newProvider.getWifiConfig().configKey()); + verify(mWifiConfigManager).saveToStore(true); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + + // Verify data source content. + List<PasspointProvider> newProviders = mUserDataSource.getProviders(); + assertEquals(1, newProviders.size()); + assertEquals(newConfig, newProviders.get(0).getConfig()); + assertEquals(2, mSharedDataSource.getProviderIndex()); + } + + /** + * Verify that adding a suggestion provider with the same base domain as the existing provider + * from different apps will fail, and verify that the existing provider is not replaced by the + * new provider with the new configuration. + * + * @throws Exception + */ + @Test + public void addSuggestionProviderWithExistingConfigFromDifferentSource() throws Exception { + // Add a provider with the original configuration. + PasspointConfiguration origConfig = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, + TEST_REALM); + PasspointProvider origProvider = createMockProvider(origConfig); + when(origProvider.getPackageName()).thenReturn(TEST_PACKAGE); + when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(false))).thenReturn(origProvider); + assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID, TEST_PACKAGE, false)); + verifyInstalledConfig(origConfig); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + reset(mWifiMetrics); + reset(mWifiConfigManager); + + // Verify data source content. + List<PasspointProvider> origProviders = mUserDataSource.getProviders(); + assertEquals(1, origProviders.size()); + assertEquals(origConfig, origProviders.get(0).getConfig()); + assertEquals(1, mSharedDataSource.getProviderIndex()); + + // Add another provider with the same base domain as the existing saved provider but from + // different app. This should not replace the existing provider with the new configuration. + PasspointConfiguration newConfig = createTestConfigWithUserCredential(TEST_FQDN, + TEST_FRIENDLY_NAME); + PasspointProvider newProvider = createMockProvider(newConfig); + when(newProvider.isFromSuggestion()).thenReturn(true); + when(newProvider.getPackageName()).thenReturn(TEST_PACKAGE1); + when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE1), + eq(true))).thenReturn(newProvider); + assertFalse(mManager.addOrUpdateProvider(newConfig, TEST_CREATOR_UID, TEST_PACKAGE1, true)); + verify(mWifiConfigManager, never()).removePasspointConfiguredNetwork( + newProvider.getWifiConfig().configKey()); + verify(mWifiConfigManager, never()).saveToStore(true); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess(); + + // Verify data source content. + List<PasspointProvider> newProviders = mUserDataSource.getProviders(); + assertEquals(1, newProviders.size()); + assertEquals(origConfig, newProviders.get(0).getConfig()); + assertEquals(2, mSharedDataSource.getProviderIndex()); + } + + /** + * Verify that an expected map of FQDN and a list of ScanResult will be returned when provided + * scanResults are matched to installed Passpoint profiles. If matched Passpoint profiles is + * from suggestion, will check if it is approved. If it is not approved, send the user approved + * notification, and not to add into the matched list. + */ + @Test + public void getAllMatchingFqdnsForScanResultsWithSuggestionProvider() { + // static mocking + MockitoSession session = + com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession().mockStatic( + InformationElementUtil.class).startMocking(); + try { + PasspointProvider providerApproved = addTestProvider(TEST_FQDN + 0, TEST_FRIENDLY_NAME, + TEST_PACKAGE); + providerApproved.getWifiConfig().isHomeProviderNetwork = true; + PasspointProvider providerNeedApprove = addTestProvider(TEST_FQDN + 1, + TEST_FRIENDLY_NAME, TEST_PACKAGE1); + providerNeedApprove.getWifiConfig().isHomeProviderNetwork = true; + + ANQPData entry = new ANQPData(mClock, null); + InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa(); + vsa.anqpDomainID = TEST_ANQP_DOMAIN_ID2; + + when(mAnqpCache.getEntry(TEST_ANQP_KEY2)).thenReturn(entry); + when(InformationElementUtil.getHS2VendorSpecificIE(isNull())).thenReturn(vsa); + when(providerApproved.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.HomeProvider); + when(providerNeedApprove.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.HomeProvider); + when(providerApproved.isFromSuggestion()).thenReturn(true); + when(providerNeedApprove.isFromSuggestion()).thenReturn(true); + when(mWifiNetworkSuggestionsManager + .sendUserApprovalNotificationIfNotApproved(eq(TEST_PACKAGE), anyInt())) + .thenReturn(false); + when(mWifiNetworkSuggestionsManager + .sendUserApprovalNotificationIfNotApproved(eq(TEST_PACKAGE1), anyInt())) + .thenReturn(true); + Map<String, Map<Integer, List<ScanResult>>> configs = + mManager.getAllMatchingFqdnsForScanResults( + createTestScanResults()); + verify(mWifiNetworkSuggestionsManager, times(2)) + .sendUserApprovalNotificationIfNotApproved(eq(TEST_PACKAGE), anyInt()); + verify(mWifiNetworkSuggestionsManager, times(2)) + .sendUserApprovalNotificationIfNotApproved(eq(TEST_PACKAGE1), anyInt()); + // Expects to be matched with home Provider for each AP (two APs). + assertEquals(2, configs.get(TEST_FQDN + 0).get( + WifiManager.PASSPOINT_HOME_NETWORK).size()); + assertFalse( + configs.get(TEST_FQDN + 0).containsKey(WifiManager.PASSPOINT_ROAMING_NETWORK)); + + // Expects there is no matched AP. + assertNull(configs.get(TEST_FQDN + 1)); + } finally { + session.finishMocking(); + } + } + + /** + * Verify that the HomeProvider provider will be returned when a HomeProvider profile has + * not expired and RoamingProvider expiration is unset (still valid). + * + * @throws Exception + */ + @Test + public void matchHomeProviderWhenHomeProviderNotExpired() throws Exception { + // static mocking + MockitoSession session = + com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession().mockStatic( + InformationElementUtil.class).startMocking(); + try { + PasspointProvider providerHome = addTestProvider(TEST_FQDN, TEST_FRIENDLY_NAME, + TEST_PACKAGE); + providerHome.getConfig().setSubscriptionExpirationTimeInMillis( + System.currentTimeMillis() + 100000); + providerHome.getWifiConfig().isHomeProviderNetwork = true; + PasspointProvider providerRoaming = addTestProvider(TEST_FQDN2, TEST_FRIENDLY_NAME, + TEST_PACKAGE); + PasspointProvider providerNone = addTestProvider(TEST_FQDN + 2, TEST_FRIENDLY_NAME, + TEST_PACKAGE, new WifiConfiguration()); + ANQPData entry = new ANQPData(mClock, null); + InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa(); + vsa.anqpDomainID = TEST_ANQP_DOMAIN_ID; + + when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry); + when(InformationElementUtil.getHS2VendorSpecificIE(isNull())).thenReturn(vsa); + when(providerHome.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.HomeProvider); + when(providerRoaming.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.RoamingProvider); + when(providerNone.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.None); + + Pair<PasspointProvider, PasspointMatch> result = + mManager.matchProvider(createTestScanResult()); + + assertEquals(PasspointMatch.HomeProvider, result.second); + assertEquals(TEST_FQDN, result.first.getConfig().getHomeSp().getFqdn()); + + } finally { + session.finishMocking(); + } + } + + /** + * Verify that the RoamingProvider provider will be returned when a HomeProvider profile has + * expired and RoamingProvider expiration is unset (still valid). + * + * @throws Exception + */ + @Test + public void matchRoamingProviderUnsetWhenHomeProviderExpired() throws Exception { + // static mocking + MockitoSession session = + com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession().mockStatic( + InformationElementUtil.class).startMocking(); + try { + PasspointProvider providerHome = addTestProvider(TEST_FQDN, TEST_FRIENDLY_NAME, + TEST_PACKAGE); + providerHome.getConfig().setSubscriptionExpirationTimeInMillis( + System.currentTimeMillis() - 10000); + providerHome.getWifiConfig().isHomeProviderNetwork = true; + PasspointProvider providerRoaming = addTestProvider(TEST_FQDN2, TEST_FRIENDLY_NAME, + TEST_PACKAGE); + PasspointProvider providerNone = addTestProvider(TEST_FQDN + 2, TEST_FRIENDLY_NAME, + TEST_PACKAGE, new WifiConfiguration()); + ANQPData entry = new ANQPData(mClock, null); + InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa(); + vsa.anqpDomainID = TEST_ANQP_DOMAIN_ID; + + when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry); + when(InformationElementUtil.getHS2VendorSpecificIE(isNull())).thenReturn(vsa); + when(providerHome.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.HomeProvider); + when(providerRoaming.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.RoamingProvider); + when(providerNone.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.None); + + Pair<PasspointProvider, PasspointMatch> result = + mManager.matchProvider(createTestScanResult()); + + assertEquals(PasspointMatch.RoamingProvider, result.second); + assertEquals(TEST_FQDN2, result.first.getConfig().getHomeSp().getFqdn()); + + } finally { + session.finishMocking(); + } + } + + /** + * Verify that the RoamingProvider provider will be returned when a HomeProvider profile has + * expired and RoamingProvider expiration is still valid. + * + * @throws Exception + */ + @Test + public void matchRoamingProviderNonExpiredWhenHomeProviderExpired() throws Exception { + // static mocking + MockitoSession session = + com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession().mockStatic( + InformationElementUtil.class).startMocking(); + try { + PasspointProvider providerHome = addTestProvider(TEST_FQDN, TEST_FRIENDLY_NAME, + TEST_PACKAGE); + providerHome.getConfig().setSubscriptionExpirationTimeInMillis( + System.currentTimeMillis() - 10000); + providerHome.getWifiConfig().isHomeProviderNetwork = true; + PasspointProvider providerRoaming = addTestProvider(TEST_FQDN2, TEST_FRIENDLY_NAME, + TEST_PACKAGE); + providerRoaming.getConfig().setSubscriptionExpirationTimeInMillis( + System.currentTimeMillis() + 100000); + PasspointProvider providerNone = addTestProvider(TEST_FQDN + 2, TEST_FRIENDLY_NAME, + TEST_PACKAGE, new WifiConfiguration()); + ANQPData entry = new ANQPData(mClock, null); + InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa(); + vsa.anqpDomainID = TEST_ANQP_DOMAIN_ID; + + when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry); + when(InformationElementUtil.getHS2VendorSpecificIE(isNull())).thenReturn(vsa); + when(providerHome.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.HomeProvider); + when(providerRoaming.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.RoamingProvider); + when(providerNone.match(anyMap(), isNull())) + .thenReturn(PasspointMatch.None); + + Pair<PasspointProvider, PasspointMatch> result = + mManager.matchProvider(createTestScanResult()); + + assertEquals(PasspointMatch.RoamingProvider, result.second); + assertEquals(TEST_FQDN2, result.first.getConfig().getHomeSp().getFqdn()); + + } finally { + session.finishMocking(); + } + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java index c7ce3f1278..5ceb2b76b5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java @@ -37,6 +37,7 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.pps.HomeSp; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.LocalLog; @@ -68,6 +69,7 @@ import java.util.List; */ @SmallTest public class PasspointNetworkEvaluatorTest { + // TODO(b/140763176): should extend WifiBaseTest, but if it does then it fails with NPE private static final int TEST_NETWORK_ID = 1; private static final String TEST_SSID1 = "ssid1"; private static final String TEST_SSID2 = "ssid2"; @@ -156,7 +158,8 @@ public class PasspointNetworkEvaluatorTest { when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager); when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mDataTelephonyManager); // SIM is present - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[1]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(mock(SubscriptionInfo.class))); when(mDataTelephonyManager.getSimOperator()).thenReturn("123456"); when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); } @@ -234,7 +237,8 @@ public class PasspointNetworkEvaluatorTest { assertNotNull(addedConfig.getValue().enterpriseConfig); assertEquals("", addedConfig.getValue().enterpriseConfig.getAnonymousIdentity()); assertTrue(addedConfig.getValue().isHomeProviderNetwork); - verify(mWifiConfigManager).enableNetwork(eq(TEST_NETWORK_ID), eq(false), anyInt()); + verify(mWifiConfigManager).enableNetwork( + eq(TEST_NETWORK_ID), eq(false), anyInt(), any()); verify(mWifiConfigManager).setNetworkCandidateScanResult( eq(TEST_NETWORK_ID), any(ScanResult.class), anyInt()); verify(mWifiConfigManager).updateScanDetailForNetwork( @@ -277,7 +281,8 @@ public class PasspointNetworkEvaluatorTest { assertNotNull(addedConfig.getValue().enterpriseConfig); assertEquals("", addedConfig.getValue().enterpriseConfig.getAnonymousIdentity()); assertFalse(addedConfig.getValue().isHomeProviderNetwork); - verify(mWifiConfigManager).enableNetwork(eq(TEST_NETWORK_ID), eq(false), anyInt()); + verify(mWifiConfigManager).enableNetwork( + eq(TEST_NETWORK_ID), eq(false), anyInt(), any()); verify(mWifiConfigManager).setNetworkCandidateScanResult( eq(TEST_NETWORK_ID), any(ScanResult.class), anyInt()); verify(mWifiConfigManager).updateScanDetailForNetwork( @@ -322,7 +327,8 @@ public class PasspointNetworkEvaluatorTest { assertNotNull(addedConfig.getValue().enterpriseConfig); assertEquals("", addedConfig.getValue().enterpriseConfig.getAnonymousIdentity()); assertTrue(addedConfig.getValue().isHomeProviderNetwork); - verify(mWifiConfigManager).enableNetwork(eq(TEST_NETWORK_ID), eq(false), anyInt()); + verify(mWifiConfigManager).enableNetwork( + eq(TEST_NETWORK_ID), eq(false), anyInt(), any()); verify(mWifiConfigManager).setNetworkCandidateScanResult( eq(TEST_NETWORK_ID), any(ScanResult.class), anyInt()); verify(mWifiConfigManager).updateScanDetailForNetwork( @@ -387,7 +393,8 @@ public class PasspointNetworkEvaluatorTest { when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(homeProvider); when(testProvider.isSimCredential()).thenReturn(true); // SIM is absent - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[0]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Collections.emptyList()); assertEquals(null, mEvaluator.evaluateNetworks( scanDetails, null, null, false, false, mOnConnectableListener)); @@ -416,7 +423,8 @@ public class PasspointNetworkEvaluatorTest { when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(homeProvider); when(testProvider.isSimCredential()).thenReturn(true); // SIM is present - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[1]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(mock(SubscriptionInfo.class))); when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID)); @@ -456,7 +464,8 @@ public class PasspointNetworkEvaluatorTest { List<ScanDetail> scanDetails = Collections.singletonList( generateScanDetail(TEST_SSID1, TEST_BSSID1)); // SIM is absent - when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[0]); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(mock(SubscriptionInfo.class))); when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false); when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java index 1bea1a2797..ff04874981 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java @@ -25,6 +25,7 @@ import android.net.wifi.ScanResult; import androidx.test.filters.SmallTest; import com.android.server.wifi.ScanDetail; +import com.android.server.wifi.WifiBaseTest; 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; @@ -42,7 +43,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointNetworkScore}. */ @SmallTest -public class PasspointNetworkScoreTest { +public class PasspointNetworkScoreTest extends WifiBaseTest { private static class TestData { public final boolean isHomeProvider; public final boolean isActiveNetwork; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java index 31229c5628..ec05332141 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java @@ -18,7 +18,6 @@ package com.android.server.wifi.hotspot2; 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.verify; import static org.mockito.Mockito.when; @@ -36,9 +35,11 @@ import android.util.Base64; import androidx.test.filters.SmallTest; +import com.android.internal.util.ArrayUtils; import com.android.server.wifi.FakeKeys; import com.android.server.wifi.IMSIParameter; import com.android.server.wifi.SIMAccessor; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiKeyStore; import com.android.server.wifi.hotspot2.anqp.ANQPElement; import com.android.server.wifi.hotspot2.anqp.CellularNetwork; @@ -75,7 +76,7 @@ import java.util.Set; */ @SmallTest @RunWith(Parameterized.class) -public class PasspointProviderTest { +public class PasspointProviderTest extends WifiBaseTest { private static final long PROVIDER_ID = 12L; private static final int CREATOR_UID = 1234; private static final String CREATOR_PACKAGE = "com.android.test"; @@ -89,6 +90,33 @@ public class PasspointProviderTest { private static final String CLIENT_CERTIFICATE_ALIAS = "HS2_12"; private static final String CLIENT_PRIVATE_KEY_ALIAS = "HS2_12"; private static final String REMEDIATION_CA_CERTIFICATE_ALIAS = "HS2_REMEDIATION_12"; + private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts"; + + private static final int TEST_UPDATE_IDENTIFIER = 1234; + private static final int TEST_USAGE_LIMIT_DATA_LIMIT = 100; + private static final String TEST_FQDN = "test.com"; + private static final String TEST_FQDN2 = "test2.com"; + private static final String TEST_FRIENDLY_NAME = "Friendly Name"; + private static final long[] TEST_RC_OIS = new long[] {0x1234L, 0x2345L}; + private static final long[] TEST_IE_RC_OIS = new long[] {0x1234L, 0x2133L}; + private static final long[] TEST_IE_NO_MATCHED_RC_OIS = new long[] {0x2255L, 0x2133L}; + private static final Long[] TEST_ANQP_RC_OIS = new Long[] {0x1234L, 0x2133L}; + private static final String TEST_REALM = "realm.com"; + private static final String[] TEST_TRUSTED_NAME = + new String[] {"trusted.fqdn.com", "another.fqdn.com"}; + // User credential data + private static final String TEST_USERNAME = "username"; + private static final String TEST_PASSWORD = "password"; + // SIM credential data + private static final int TEST_EAP_TYPE = WifiEnterpriseConfig.Eap.SIM; + private static final int TEST_SIM_CREDENTIAL_TYPE = EAPConstants.EAP_SIM; + private static final String TEST_IMSI = "1234567890"; + + private enum CredentialType { + USER, + CERT, + SIM + } @Mock WifiKeyStore mKeyStore; @Mock SIMAccessor mSimAccessor; @@ -126,7 +154,7 @@ public class PasspointProviderTest { */ private PasspointProvider createProvider(PasspointConfiguration config) { return new PasspointProvider(config, mKeyStore, mSimAccessor, PROVIDER_ID, CREATOR_UID, - CREATOR_PACKAGE); + CREATOR_PACKAGE, false); } /** @@ -199,27 +227,222 @@ public class PasspointProviderTest { } /** - * Verify that modification to the configuration used for creating PasspointProvider - * will not change the configuration stored inside the PasspointProvider. + * Helper function for generating test passpoint configuration for test cases * + * @param credentialType which type credential is generated. + * @param isLegacy if true, omit some passpoint fields to avoid breaking comparison + * between passpoint configuration converted from wifi configuration + * and generated passpoint configuration. + * @return a valid passpoint configuration * @throws Exception */ - @Test - public void verifyModifyOriginalConfig() throws Exception { - // Create a dummy PasspointConfiguration. + private PasspointConfiguration generateTestPasspointConfiguration( + CredentialType credentialType, boolean isLegacy) throws Exception { PasspointConfiguration config = new PasspointConfiguration(); + if (!isLegacy) { + config.setUpdateIdentifier(TEST_UPDATE_IDENTIFIER); + config.setUsageLimitDataLimit(TEST_USAGE_LIMIT_DATA_LIMIT); + } HomeSp homeSp = new HomeSp(); - homeSp.setFqdn("test1"); + homeSp.setFqdn(TEST_FQDN); + homeSp.setFriendlyName(TEST_FRIENDLY_NAME); + homeSp.setRoamingConsortiumOis(TEST_RC_OIS); config.setHomeSp(homeSp); Credential credential = new Credential(); - credential.setUserCredential(new Credential.UserCredential()); + credential.setRealm(TEST_REALM); + + if (credentialType == CredentialType.USER) { + byte[] base64EncodedPw = + Base64.encode(TEST_PASSWORD.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT); + String encodedPasswordStr = new String(base64EncodedPw, StandardCharsets.UTF_8); + Credential.UserCredential userCredential = new Credential.UserCredential(); + userCredential.setEapType(EAPConstants.EAP_TTLS); + userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2); + userCredential.setUsername(TEST_USERNAME); + userCredential.setPassword(encodedPasswordStr); + if (!isLegacy) { + credential.setCaCertificates(new X509Certificate[] {FakeKeys.CA_CERT0}); + } + credential.setUserCredential(userCredential); + } else if (credentialType == CredentialType.CERT) { + Credential.CertificateCredential certCredential = + new Credential.CertificateCredential(); + if (!isLegacy) { + certCredential.setCertSha256Fingerprint( + MessageDigest.getInstance("SHA-256") + .digest(FakeKeys.CLIENT_CERT.getEncoded())); + credential.setCaCertificates(new X509Certificate[] {FakeKeys.CA_CERT0}); + credential.setClientPrivateKey(FakeKeys.RSA_KEY1); + credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT}); + } else { + certCredential.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3); + } + credential.setCertCredential(certCredential); + } else if (credentialType == CredentialType.SIM) { + 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 verifying wifi configuration based on passpoing configuration + * + * @param passpointConfig the source of wifi configuration. + * @param wifiConfig wifi configuration be verified. + * + * @throws Exception + */ + private void verifyWifiConfigWithTestData( + PasspointConfiguration passpointConfig, WifiConfiguration wifiConfig) { + BitSet allowedProtocols = new BitSet(); + allowedProtocols.set(WifiConfiguration.Protocol.RSN); + + HomeSp homeSp = passpointConfig.getHomeSp(); + Credential credential = passpointConfig.getCredential(); + + // Need to verify field by field since WifiConfiguration doesn't + // override equals() function. + WifiEnterpriseConfig wifiEnterpriseConfig = wifiConfig.enterpriseConfig; + assertEquals(homeSp.getFqdn(), wifiConfig.FQDN); + assertEquals(homeSp.getFriendlyName(), wifiConfig.providerFriendlyName); + assertTrue(Arrays.equals(homeSp.getRoamingConsortiumOis(), + wifiConfig.roamingConsortiumIds)); + + assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)); + assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)); + assertEquals(wifiConfig.updateIdentifier, + Integer.toString(passpointConfig.getUpdateIdentifier())); + assertEquals(allowedProtocols, wifiConfig.allowedProtocols); + assertEquals(Integer.toString(passpointConfig.getUpdateIdentifier()), + wifiConfig.updateIdentifier); + assertFalse(wifiConfig.shared); + assertEquals(credential.getRealm(), wifiEnterpriseConfig.getRealm()); + + if (credential.getUserCredential() != null) { + Credential.UserCredential userCredential = credential.getUserCredential(); + byte[] pwOctets = Base64.decode(userCredential.getPassword(), Base64.DEFAULT); + String decodedPassword = new String(pwOctets, StandardCharsets.UTF_8); + + assertEquals("anonymous@" + credential.getRealm(), + wifiEnterpriseConfig.getAnonymousIdentity()); + assertEquals(WifiEnterpriseConfig.Eap.TTLS, wifiEnterpriseConfig.getEapMethod()); + switch (userCredential.getNonEapInnerMethod()) { + case Credential.UserCredential.AUTH_METHOD_PAP: + assertEquals(WifiEnterpriseConfig.Phase2.PAP, + wifiEnterpriseConfig.getPhase2Method()); + break; + case Credential.UserCredential.AUTH_METHOD_MSCHAP: + assertEquals(WifiEnterpriseConfig.Phase2.MSCHAP, + wifiEnterpriseConfig.getPhase2Method()); + break; + case Credential.UserCredential.AUTH_METHOD_MSCHAPV2: + assertEquals(WifiEnterpriseConfig.Phase2.MSCHAPV2, + wifiEnterpriseConfig.getPhase2Method()); + break; + } + assertEquals(userCredential.getUsername(), wifiEnterpriseConfig.getIdentity()); + assertEquals(decodedPassword, wifiEnterpriseConfig.getPassword()); + assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED, wifiConfig.meteredOverride); + + if (!ArrayUtils.isEmpty(passpointConfig.getAaaServerTrustedNames())) { + assertEquals(String.join(";", passpointConfig.getAaaServerTrustedNames()), + wifiEnterpriseConfig.getDomainSuffixMatch()); + } else { + assertEquals(homeSp.getFqdn(), wifiEnterpriseConfig.getDomainSuffixMatch()); + } + + if (!ArrayUtils.isEmpty(passpointConfig.getAaaServerTrustedNames())) { + assertTrue(Arrays.equals(new String[] {SYSTEM_CA_STORE_PATH}, + wifiEnterpriseConfig.getCaCertificateAliases())); + } else if (ArrayUtils.isEmpty(credential.getCaCertificates())) { + assertTrue(Arrays.equals(new String[] {SYSTEM_CA_STORE_PATH}, + wifiEnterpriseConfig.getCaCertificateAliases())); + } else { + assertEquals(CA_CERTIFICATE_ALIAS, wifiEnterpriseConfig.getCaCertificateAlias()); + } + if (passpointConfig.getCredential().getCheckAaaServerCertStatus()) { + assertEquals(wifiEnterpriseConfig.getOcsp(), + WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); + } else { + assertEquals(wifiEnterpriseConfig.getOcsp(), + WifiEnterpriseConfig.OCSP_NONE); + } + } else if (credential.getCertCredential() != null) { + Credential.CertificateCredential certCredential = credential.getCertCredential(); + assertEquals("anonymous@" + credential.getRealm(), + wifiEnterpriseConfig.getAnonymousIdentity()); + assertEquals(WifiEnterpriseConfig.Eap.TLS, wifiEnterpriseConfig.getEapMethod()); + assertEquals(CLIENT_CERTIFICATE_ALIAS, + wifiEnterpriseConfig.getClientCertificateAlias()); + assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED, wifiConfig.meteredOverride); + // Domain suffix match + if (ArrayUtils.isEmpty(passpointConfig.getAaaServerTrustedNames())) { + assertEquals(homeSp.getFqdn(), wifiEnterpriseConfig.getDomainSuffixMatch()); + } else { + assertEquals(String.join(";", passpointConfig.getAaaServerTrustedNames()), + wifiEnterpriseConfig.getDomainSuffixMatch()); + assertTrue(Arrays.equals(new String[] {SYSTEM_CA_STORE_PATH}, + wifiEnterpriseConfig.getCaCertificateAliases())); + } + // CA certificate + if (!ArrayUtils.isEmpty(passpointConfig.getAaaServerTrustedNames())) { + assertTrue(Arrays.equals(new String[] {SYSTEM_CA_STORE_PATH}, + wifiEnterpriseConfig.getCaCertificateAliases())); + } else if (!ArrayUtils.isEmpty(credential.getCaCertificates())) { + assertEquals(CA_CERTIFICATE_ALIAS, wifiEnterpriseConfig.getCaCertificateAlias()); + } else { + assertTrue(Arrays.equals(new String[] {SYSTEM_CA_STORE_PATH}, + wifiEnterpriseConfig.getCaCertificateAliases())); + } + if (passpointConfig.getCredential().getCheckAaaServerCertStatus()) { + assertEquals(wifiEnterpriseConfig.getOcsp(), + WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); + } else { + assertEquals(wifiEnterpriseConfig.getOcsp(), + WifiEnterpriseConfig.OCSP_NONE); + } + } else if (credential.getSimCredential() != null) { + Credential.SimCredential simCredential = credential.getSimCredential(); + switch (simCredential.getEapType()) { + case EAPConstants.EAP_SIM: + assertEquals(WifiEnterpriseConfig.Eap.SIM, + wifiEnterpriseConfig.getEapMethod()); + break; + case EAPConstants.EAP_AKA: + assertEquals(WifiEnterpriseConfig.Eap.AKA, + wifiEnterpriseConfig.getEapMethod()); + break; + case EAPConstants.EAP_AKA_PRIME: + assertEquals(WifiEnterpriseConfig.Eap.AKA_PRIME, + wifiEnterpriseConfig.getEapMethod()); + break; + } + assertEquals(simCredential.getImsi(), wifiEnterpriseConfig.getPlmn()); + } + } + + /** + * 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 = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); verifyInstalledConfig(config, true); // Modify the original configuration, the configuration maintained by the provider // should be unchanged. - config.getHomeSp().setFqdn("test2"); + config.getHomeSp().setFqdn(TEST_FQDN2); verifyInstalledConfig(config, false); } @@ -232,20 +455,15 @@ public class PasspointProviderTest { @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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); 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"); + retrievedConfig.getHomeSp().setFqdn(TEST_FQDN2); verifyInstalledConfig(retrievedConfig, false); } @@ -257,16 +475,11 @@ public class PasspointProviderTest { @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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.CERT, false); + Credential credential = config.getCredential(); + Credential.CertificateCredential certCredential = credential.getCertCredential(); credential.setCaCertificates(new X509Certificate[]{FakeKeys.CA_CERT0, FakeKeys.CA_CERT1}); - credential.setClientPrivateKey(FakeKeys.RSA_KEY1); - credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT}); - config.setCredential(credential); if (mRemediationCaCertificate != null) { UpdateParameter updateParameter = new UpdateParameter(); updateParameter.setCaCertificate(mRemediationCaCertificate); @@ -309,15 +522,11 @@ public class PasspointProviderTest { @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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.CERT, false); + Credential credential = config.getCredential(); + Credential.CertificateCredential certCredential = credential.getCertCredential(); credential.setCaCertificates(new X509Certificate[]{FakeKeys.CA_CERT0, FakeKeys.CA_CERT1}); - credential.setClientPrivateKey(FakeKeys.RSA_KEY1); - credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT}); config.setCredential(credential); UpdateParameter updateParameter = new UpdateParameter(); @@ -359,15 +568,11 @@ public class PasspointProviderTest { @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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.CERT, false); + Credential credential = config.getCredential(); + Credential.CertificateCredential certCredential = credential.getCertCredential(); credential.setCaCertificates(new X509Certificate[]{FakeKeys.CA_CERT0, FakeKeys.CA_CERT1}); - credential.setClientPrivateKey(FakeKeys.RSA_KEY1); - credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT}); config.setCredential(credential); if (mRemediationCaCertificate != null) { UpdateParameter updateParameter = new UpdateParameter(); @@ -418,24 +623,15 @@ public class PasspointProviderTest { */ @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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup ANQP elements. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPDomName, - createDomainNameElement(new String[] {testDomain})); + createDomainNameElement(new String[] {TEST_FQDN})); assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap, mRoamingConsortium)); @@ -449,28 +645,17 @@ public class PasspointProviderTest { */ @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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Domain Name ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPDomName, - createDomainNameElement(new String[] {testDomain})); + createDomainNameElement(new String[] {TEST_FQDN})); anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS, + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TTLS, new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2))); assertEquals(PasspointMatch.HomeProvider, @@ -486,28 +671,17 @@ public class PasspointProviderTest { */ @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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Domain Name ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPDomName, - createDomainNameElement(new String[] {testDomain})); + createDomainNameElement(new String[] {TEST_FQDN})); anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null)); + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TLS, null)); assertEquals(PasspointMatch.None, mProvider.match(anqpElementMap, mRoamingConsortium)); } @@ -520,18 +694,11 @@ public class PasspointProviderTest { */ @Test public void matchFQDNWith3GPPNetworkDomainName() 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})); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.SIM, false); + when(mSimAccessor.getMatchingImsis(new IMSIParameter(TEST_IMSI, false))) + .thenReturn(Arrays.asList(new String[] {TEST_IMSI})); mProvider = createProvider(config); // Setup Domain Name ANQP element. @@ -552,33 +719,19 @@ public class PasspointProviderTest { */ @Test public void matchFQDNOverRoamingProvider() 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})); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.SIM, false); + when(mSimAccessor.getMatchingImsis(new IMSIParameter(TEST_IMSI, false))) + .thenReturn(Arrays.asList(new String[] {TEST_IMSI})); mProvider = createProvider(config); // Setup ANQP elements. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPDomName, - createDomainNameElement(new String[] {testDomain})); + createDomainNameElement(new String[] {TEST_FQDN})); anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium, - createRoamingConsortiumElement(anqpRCOIs)); + createRoamingConsortiumElement(TEST_ANQP_RC_OIS)); anqpElementMap.put(ANQPElementType.ANQP3GPPNetwork, createThreeGPPNetworkElement(new String[] {"123456"})); @@ -594,25 +747,15 @@ public class PasspointProviderTest { */ @Test public void matchRoamingConsortiumWithoutNAIRealm() 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.SIM, false); mProvider = createProvider(config); // Setup Roaming Consortium ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium, - createRoamingConsortiumElement(anqpRCOIs)); + createRoamingConsortiumElement(TEST_ANQP_RC_OIS)); assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap, mRoamingConsortium)); @@ -627,29 +770,18 @@ public class PasspointProviderTest { */ @Test public void matchRoamingConsortiumWithNAIRealmMatch() throws Exception { - long[] providerRCOIs = new long[] {0x1234L, 0x2345L}; - Long[] anqpRCOIs = new Long[] {0x1234L, 0x2133L}; - String testRealm = "realm.com"; // Setup test provider. - PasspointConfiguration config = new PasspointConfiguration(); - HomeSp homeSp = new HomeSp(); - homeSp.setRoamingConsortiumOis(providerRCOIs); - 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Roaming Consortium ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium, - createRoamingConsortiumElement(anqpRCOIs)); + createRoamingConsortiumElement(TEST_ANQP_RC_OIS)); anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS, + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TTLS, new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2))); assertEquals(PasspointMatch.RoamingProvider, @@ -664,30 +796,18 @@ public class PasspointProviderTest { */ @Test public void matchRoamingConsortiumWithNAIRealmMisMatch() throws Exception { - long[] providerRCOIs = new long[] {0x1234L, 0x2345L}; - Long[] anqpRCOIs = new Long[] {0x1234L, 0x2133L}; - String testRealm = "realm.com"; - // Setup test provider. - PasspointConfiguration config = new PasspointConfiguration(); - HomeSp homeSp = new HomeSp(); - homeSp.setRoamingConsortiumOis(providerRCOIs); - 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Roaming Consortium ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium, - createRoamingConsortiumElement(anqpRCOIs)); + createRoamingConsortiumElement(TEST_ANQP_RC_OIS)); // Set up NAI with different EAP method anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null)); + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TLS, null)); assertEquals(PasspointMatch.None, mProvider.match(anqpElementMap, mRoamingConsortium)); @@ -701,26 +821,16 @@ public class PasspointProviderTest { */ @Test public void matchRoamingConsortiumIeWithoutNAIRealm() throws Exception { - long[] providerRCOIs = new long[] {0x1234L, 0x2345L}; - long[] ieRCOIs = 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Roaming Consortium ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); // Setup Roaming Consortium Information element. - when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(ieRCOIs); + when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(TEST_IE_RC_OIS); assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap, mRoamingConsortium)); @@ -735,30 +845,18 @@ public class PasspointProviderTest { */ @Test public void matchRoamingConsortiumIeWithNAIRealmMatch() throws Exception { - long[] providerRCOIs = new long[] {0x1234L, 0x2345L}; - long[] ieRCOIs = new long[] {0x1234L, 0x2133L}; - String testRealm = "realm.com"; - // Setup test provider. - PasspointConfiguration config = new PasspointConfiguration(); - HomeSp homeSp = new HomeSp(); - homeSp.setRoamingConsortiumOis(providerRCOIs); - 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Roaming Consortium ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); // Setup Roaming Consortium Information element. - when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(ieRCOIs); + when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(TEST_IE_RC_OIS); anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS, + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TTLS, new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2))); assertEquals(PasspointMatch.RoamingProvider, @@ -773,31 +871,19 @@ public class PasspointProviderTest { */ @Test public void matchRoamingConsortiumIeWithNAIRealmMismatch() throws Exception { - long[] providerRCOIs = new long[] {0x1234L, 0x2345L}; - long[] ieRCOIs = new long[] {0x1234L, 0x2133L}; - String testRealm = "realm.com"; - // Setup test provider. - PasspointConfiguration config = new PasspointConfiguration(); - HomeSp homeSp = new HomeSp(); - homeSp.setRoamingConsortiumOis(providerRCOIs); - 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Roaming Consortium ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); // Setup Roaming Consortium Information element. - when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(ieRCOIs); + when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(TEST_IE_RC_OIS); // Set up NAI with different EAP method anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null)); + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TLS, null)); assertEquals(PasspointMatch.None, mProvider.match(anqpElementMap, mRoamingConsortium)); @@ -812,26 +898,16 @@ public class PasspointProviderTest { */ @Test public void misMatchForRoamingConsortiumIeAndNAIRealm() throws Exception { - long[] providerRCOIs = new long[] {0x1234L, 0x2345L}; - long[] ieRCOIs = new long[] {0x2255L, 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup Roaming Consortium ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); // Setup Roaming Consortium Information element. - when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(ieRCOIs); + when(mRoamingConsortium.getRoamingConsortiums()).thenReturn(TEST_IE_NO_MATCHED_RC_OIS); assertEquals(PasspointMatch.None, mProvider.match(anqpElementMap, mRoamingConsortium)); @@ -846,18 +922,11 @@ public class PasspointProviderTest { */ @Test public void matchThreeGPPNetworkWithNAIRealmMismatch() throws Exception { - String testImsi = "1234567890"; - String testRealm = "realm.com"; // 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})); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.SIM, false); + when(mSimAccessor.getMatchingImsis(new IMSIParameter(TEST_IMSI, false))) + .thenReturn(Arrays.asList(new String[] {TEST_IMSI})); mProvider = createProvider(config); // Setup 3GPP Network ANQP element. @@ -867,7 +936,7 @@ public class PasspointProviderTest { // Setup NAI Realm ANQP element with different realm. anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS, + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TTLS, new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2))); assertEquals(PasspointMatch.RoamingProvider, @@ -883,19 +952,11 @@ public class PasspointProviderTest { */ @Test public void matchThreeGPPNetworkWithNAIRealmMatch() throws Exception { - String testImsi = "1234567890"; - String testRealm = "realm.com"; // 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); - credential.setRealm(testRealm); - when(mSimAccessor.getMatchingImsis(new IMSIParameter(testImsi, false))) - .thenReturn(Arrays.asList(new String[] {testImsi})); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.SIM, false); + when(mSimAccessor.getMatchingImsis(new IMSIParameter(TEST_IMSI, false))) + .thenReturn(Arrays.asList(new String[] {TEST_IMSI})); mProvider = createProvider(config); // Setup 3GPP Network ANQP element. @@ -905,7 +966,7 @@ public class PasspointProviderTest { // Setup NAI Realm ANQP element with same realm. anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_AKA, null)); + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_AKA, null)); assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap, mRoamingConsortium)); @@ -919,69 +980,65 @@ public class PasspointProviderTest { */ @Test public void matchOnlyNAIRealmWithOtherInformationMismatch() 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); // Setup NAI Realm ANQP element. Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); anqpElementMap.put(ANQPElementType.ANQPNAIRealm, - createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS, + createNAIRealmElement(TEST_REALM, EAPConstants.EAP_TTLS, new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2))); assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap, mRoamingConsortium)); } - /** * Verify that an expected WifiConfiguration will be returned for a Passpoint provider - * with an user credential. + * with a 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); - BitSet allowedProtocols = new BitSet(); - allowedProtocols.set(WifiConfiguration.Protocol.RSN); + // Create provider for R2. + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); + 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. + verifyWifiConfigWithTestData(config, mProvider.getWifiConfig()); + + // Verify that AAA server trusted names are provisioned. + config.setAaaServerTrustedNames(TEST_TRUSTED_NAME); + mProvider = createProvider(config); + verifyWifiConfigWithTestData(config, + createProvider(config).getWifiConfig()); + } + + /** + * Verify that an expected WifiConfiguration will be returned for a Passpoint provider + * with a user credential which has AAA server trusted names provisioned. + * + * @throws Exception + */ + @Test + public void getWifiConfigWithUserCredentialHasAaaServerTrustedNames() throws Exception { // Create provider for R2. - PasspointConfiguration config = new PasspointConfiguration(); - config.setUpdateIdentifier(1234); - config.setUsageLimitDataLimit(100); - 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.setCaCertificates(new X509Certificate[] {FakeKeys.CA_CERT0}); - config.setCredential(credential); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); + config.setAaaServerTrustedNames(TEST_TRUSTED_NAME); + Credential credential = config.getCredential(); + // OCSP (Online Certificate Status Protocol) is required. + credential.setCheckAaaServerCertStatus(true); mProvider = createProvider(config); // Install certificate. @@ -990,28 +1047,33 @@ public class PasspointProviderTest { 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(wifiConfig.updateIdentifier, Integer.toString(config.getUpdateIdentifier())); - assertEquals(allowedProtocols, wifiConfig.allowedProtocols); - assertEquals("1234", wifiConfig.updateIdentifier); - assertFalse(wifiConfig.shared); - 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(WifiConfiguration.METERED_OVERRIDE_METERED, wifiConfig.meteredOverride); - assertEquals(CA_CERTIFICATE_ALIAS, wifiEnterpriseConfig.getCaCertificateAlias()); + // the configuration. + verifyWifiConfigWithTestData(config, mProvider.getWifiConfig()); + + // Verify that AAA server trusted names are provisioned. + config.setAaaServerTrustedNames(TEST_TRUSTED_NAME); + mProvider = createProvider(config); + verifyWifiConfigWithTestData(config, + createProvider(config).getWifiConfig()); + } + + /** + * Verify that an expected WifiConfiguration will be returned for a Passpoint provider + * with a user credential which has no CA cert. + * + * @throws Exception + */ + @Test + public void getWifiConfigWithUserCredentialNoCaCert() throws Exception { + // Create provider for R2. + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); + config.getCredential().setCaCertificates(null); + mProvider = createProvider(config); + + // Retrieve the WifiConfiguration associated with the provider, and verify the content of + // the configuration. + verifyWifiConfigWithTestData(config, mProvider.getWifiConfig()); } /** @@ -1022,33 +1084,37 @@ public class PasspointProviderTest { */ @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"; - BitSet allowedProtocols = new BitSet(); - allowedProtocols.set(WifiConfiguration.Protocol.RSN); + // Create provider. + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.CERT, false); + 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. + verifyWifiConfigWithTestData(config, mProvider.getWifiConfig()); + } + + /** + * Verify that an expected WifiConfiguration will be returned for a Passpoint provider + * with a certificate credential which has AAA server trusted names provisioned. + * + * @throws Exception + */ + @Test + public void getWifiConfigWithCertCredentialHasAaaServerTrustedNames() throws Exception { // Create provider. - PasspointConfiguration config = new PasspointConfiguration(); - config.setUpdateIdentifier(1234); - config.setUsageLimitTimeLimitInMinutes(100); - 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.setCaCertificates(new X509Certificate[] {FakeKeys.CA_CERT0}); - credential.setClientPrivateKey(FakeKeys.RSA_KEY1); - credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT}); - config.setCredential(credential); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.CERT, false); + config.setAaaServerTrustedNames(TEST_TRUSTED_NAME); mProvider = createProvider(config); // Install certificate. @@ -1061,25 +1127,34 @@ public class PasspointProviderTest { 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(allowedProtocols, wifiConfig.allowedProtocols); - assertEquals("1234", wifiConfig.updateIdentifier); - assertFalse(wifiConfig.shared); - 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()); - assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED, wifiConfig.meteredOverride); + // the configuration. + verifyWifiConfigWithTestData(config, mProvider.getWifiConfig()); + } + + /** + * Verify that an expected WifiConfiguration will be returned for a Passpoint provider + * with a certificate credential which has no CA cert. + * + * @throws Exception + */ + @Test + public void getWifiConfigWithCertCredentialNoCaCert() throws Exception { + // Create provider. + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.CERT, false); + config.getCredential().setCaCertificates(null); + mProvider = createProvider(config); + + // Install certificate. + 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. + verifyWifiConfigWithTestData(config, mProvider.getWifiConfig()); } /** @@ -1090,97 +1165,41 @@ public class PasspointProviderTest { */ @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*"; - BitSet allowedProtocols = new BitSet(); - allowedProtocols.set(WifiConfiguration.Protocol.RSN); - // 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); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.SIM, false); 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(allowedProtocols, wifiConfig.allowedProtocols); - assertNull(wifiConfig.updateIdentifier); - assertFalse(wifiConfig.shared); - assertEquals(realm, wifiEnterpriseConfig.getRealm()); - assertEquals(fqdn, wifiEnterpriseConfig.getDomainSuffixMatch()); - assertEquals(WifiEnterpriseConfig.Eap.SIM, wifiEnterpriseConfig.getEapMethod()); - assertEquals(imsi, wifiEnterpriseConfig.getPlmn()); - assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE, wifiConfig.meteredOverride); + // the configuration. + verifyWifiConfigWithTestData(config, mProvider.getWifiConfig()); } /** * Verify that an expected {@link PasspointConfiguration} will be returned when converting - * from a {@link WifiConfiguration} containing an user credential. + * from a {@link WifiConfiguration} containing a 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.FQDN = TEST_FQDN; + wifiConfig.providerFriendlyName = TEST_FRIENDLY_NAME; + wifiConfig.roamingConsortiumIds = TEST_RC_OIS; + wifiConfig.enterpriseConfig.setIdentity(TEST_USERNAME); + wifiConfig.enterpriseConfig.setPassword(TEST_PASSWORD); + wifiConfig.enterpriseConfig.setRealm(TEST_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); + PasspointConfiguration passpointConfig = generateTestPasspointConfiguration( + CredentialType.USER, true); + Credential.UserCredential userCredential = + passpointConfig.getCredential().getUserCredential(); + userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_PAP); assertEquals(passpointConfig, PasspointProvider.convertFromWifiConfig(wifiConfig)); } @@ -1193,36 +1212,18 @@ public class PasspointProviderTest { */ @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.FQDN = TEST_FQDN; + wifiConfig.providerFriendlyName = TEST_FRIENDLY_NAME; + wifiConfig.roamingConsortiumIds = TEST_RC_OIS; + wifiConfig.enterpriseConfig.setRealm(TEST_REALM); wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); - wifiConfig.enterpriseConfig.setPlmn(imsi); + wifiConfig.enterpriseConfig.setPlmn(TEST_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); + PasspointConfiguration passpointConfig = generateTestPasspointConfiguration( + CredentialType.SIM, true); assertEquals(passpointConfig, PasspointProvider.convertFromWifiConfig(wifiConfig)); } @@ -1235,33 +1236,17 @@ public class PasspointProviderTest { */ @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.FQDN = TEST_FQDN; + wifiConfig.providerFriendlyName = TEST_FRIENDLY_NAME; + wifiConfig.roamingConsortiumIds = TEST_RC_OIS; + wifiConfig.enterpriseConfig.setRealm(TEST_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); + PasspointConfiguration passpointConfig = generateTestPasspointConfiguration( + CredentialType.CERT, true); assertEquals(passpointConfig, PasspointProvider.convertFromWifiConfig(wifiConfig)); } @@ -1274,27 +1259,8 @@ public class PasspointProviderTest { */ @Test public void providerBackedBySimCredential() throws Exception { - // Test data. - String fqdn = "test.com"; - String friendlyName = "Friendly Name"; - long[] rcOIs = new long[] {0x1234L, 0x2345L}; - String realm = "realm.com"; - String imsi = "1234*"; - - // Create provider with SIM credential. - PasspointConfiguration config = new PasspointConfiguration(); - HomeSp homeSp = new HomeSp(); - homeSp.setFqdn(fqdn); - homeSp.setFriendlyName(friendlyName); - homeSp.setRoamingConsortiumOis(rcOIs); - config.setHomeSp(homeSp); - Credential credential = new Credential(); - credential.setRealm(realm); - Credential.SimCredential simCredential = new Credential.SimCredential(); - simCredential.setImsi(imsi); - simCredential.setEapType(EAPConstants.EAP_SIM); - credential.setSimCredential(simCredential); - config.setCredential(credential); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.SIM, false); mProvider = createProvider(config); assertTrue(mProvider.isSimCredential()); @@ -1308,25 +1274,8 @@ public class PasspointProviderTest { */ @Test public void providerNotBackedBySimCredential() throws Exception { - // Test data. - String fqdn = "test.com"; - String friendlyName = "Friendly Name"; - long[] rcOIs = new long[] {0x1234L, 0x2345L}; - String realm = "realm.com"; - - // Create provider with certificate credential. - PasspointConfiguration config = new PasspointConfiguration(); - HomeSp homeSp = new HomeSp(); - homeSp.setFqdn(fqdn); - homeSp.setFriendlyName(friendlyName); - homeSp.setRoamingConsortiumOis(rcOIs); - config.setHomeSp(homeSp); - Credential credential = new Credential(); - Credential.CertificateCredential certCredential = new Credential.CertificateCredential(); - certCredential.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3); - credential.setCertCredential(certCredential); - credential.setRealm(realm); - config.setCredential(credential); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.CERT, false); mProvider = createProvider(config); assertFalse(mProvider.isSimCredential()); @@ -1340,13 +1289,8 @@ public class PasspointProviderTest { */ @Test public void setHasEverConnected() throws Exception { - PasspointConfiguration config = new PasspointConfiguration(); - HomeSp homeSp = new HomeSp(); - homeSp.setFqdn("test1"); - config.setHomeSp(homeSp); - Credential credential = new Credential(); - credential.setUserCredential(new Credential.UserCredential()); - config.setCredential(credential); + PasspointConfiguration config = generateTestPasspointConfiguration( + CredentialType.USER, false); mProvider = createProvider(config); verifyInstalledConfig(config, true); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java index 64a7f9e2d1..e101aa9b0a 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java @@ -63,6 +63,7 @@ import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.org.conscrypt.TrustManagerImpl; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiMetrics; import com.android.server.wifi.WifiNative; import com.android.server.wifi.hotspot2.anqp.ANQPElement; @@ -105,7 +106,7 @@ import javax.net.ssl.SSLContext; * Unit tests for {@link PasspointProvisioner}. */ @SmallTest -public class PasspointProvisionerTest { +public class PasspointProvisionerTest extends WifiBaseTest { private static final int TEST_UID = 1500; private static final int STEP_INIT = 0; private static final int STEP_AP_CONNECT = 1; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointXmlUtilsTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointXmlUtilsTest.java index 928a491684..233a740f96 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointXmlUtilsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointXmlUtilsTest.java @@ -29,6 +29,7 @@ import android.util.Xml; import androidx.test.filters.SmallTest; import com.android.internal.util.FastXmlSerializer; +import com.android.server.wifi.WifiBaseTest; import org.junit.Test; import org.xmlpull.v1.XmlPullParser; @@ -50,7 +51,7 @@ import java.util.Map; * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointXmlUtilsTest}. */ @SmallTest -public class PasspointXmlUtilsTest { +public class PasspointXmlUtilsTest extends WifiBaseTest { /** * Helper function for generating a {@link PasspointConfiguration} for testing the XML diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ServiceProviderVerifierTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ServiceProviderVerifierTest.java index e6ecb330ec..fc4611af39 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ServiceProviderVerifierTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ServiceProviderVerifierTest.java @@ -32,14 +32,15 @@ import android.util.Pair; import androidx.test.filters.SmallTest; -import com.android.org.bouncycastle.asn1.ASN1EncodableVector; -import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; -import com.android.org.bouncycastle.asn1.DEROctetString; -import com.android.org.bouncycastle.asn1.DERSequence; -import com.android.org.bouncycastle.asn1.DERTaggedObject; -import com.android.org.bouncycastle.asn1.DERUTF8String; -import com.android.org.bouncycastle.asn1.x509.GeneralName; - +import com.android.server.wifi.WifiBaseTest; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DERUTF8String; +import org.bouncycastle.asn1.x509.GeneralName; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -55,7 +56,7 @@ import java.util.Locale; * Unit tests for {@link ServiceProviderVerifier}. */ @SmallTest -public class ServiceProviderVerifierTest { +public class ServiceProviderVerifierTest extends WifiBaseTest { private List<List<?>> mNewNames; private static final String LOCAL_HOST_NAME = "localhost"; private static final byte[] LOCAL_HOST_ADDRESS = {127, 0, 0, 1}; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/SystemInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/SystemInfoTest.java index 62d7b503f8..6acc8872c0 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/SystemInfoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/SystemInfoTest.java @@ -27,6 +27,7 @@ import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiNative; import org.junit.Before; @@ -37,7 +38,7 @@ import org.mockito.Mock; * Unit tests for {@link SystemInfo}. */ @SmallTest -public class SystemInfoTest { +public class SystemInfoTest extends WifiBaseTest { @Mock Context mContext; @Mock TelephonyManager mTelephonyManager; @Mock TelephonyManager mDataTelephonyManager; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java index 6ddeb263eb..b3e17c6176 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java @@ -18,6 +18,8 @@ package com.android.server.wifi.hotspot2; import static org.junit.Assert.*; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.util.ArrayList; @@ -25,7 +27,7 @@ import java.util.ArrayList; /** * Unit tests for {@link com.android.server.wifi.hotspot2.Utils}. */ -public class UtilsTest { +public class UtilsTest extends WifiBaseTest { @Test public void testRoamingConsortiumsToStringLong() { assertEquals("null", Utils.roamingConsortiumsToString((long[]) null)); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java index 016e322614..2aabcaa7a5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java @@ -22,6 +22,8 @@ import android.net.wifi.WifiSsid; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -39,7 +41,7 @@ import java.util.Locale; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.ANQPParser}. */ @SmallTest -public class ANQPParserTest { +public class ANQPParserTest extends WifiBaseTest { /** * Helper function for generating payload for a Venue Name ANQP element. * diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTest.java index 3b47ab4f04..1cfc85db18 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertNull; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -33,7 +35,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.CellularNetwork}. */ @SmallTest -public class CellularNetworkTest { +public class CellularNetworkTest extends WifiBaseTest { 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}; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java index e293a4a9f3..1b81749d89 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -35,7 +37,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.DomainNameElement}. */ @SmallTest -public class DomainNameElementTest { +public class DomainNameElementTest extends WifiBaseTest { private static final String TEST_DOMAIN_NAME1 = "test1.com"; private static final String TEST_DOMAIN_NAME2 = "test2.com"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java index 62a2b7f7cf..8c68c13f37 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.nio.BufferUnderflowException; @@ -33,7 +35,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSConnectionCapabilityElement}. */ @SmallTest -public class HSConnectionCapabilityElementTest { +public class HSConnectionCapabilityElementTest extends WifiBaseTest { private static final ProtocolPortTuple TEST_TUPLE1 = new ProtocolPortTuple(1, 2, ProtocolPortTuple.PROTO_STATUS_CLOSED); private static final ProtocolPortTuple TEST_TUPLE2 = diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java index dffb5a563d..256df82734 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -38,7 +40,7 @@ import java.util.Locale; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSFriendlyNameElement}. */ @SmallTest -public class HSFriendlyNameElementTest { +public class HSFriendlyNameElementTest extends WifiBaseTest { 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"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSIconFileElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSIconFileElementTest.java index a1a9aba4a8..2b77613aef 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSIconFileElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSIconFileElementTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -32,7 +34,7 @@ import java.nio.charset.StandardCharsets; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSIconFileElement}. */ @SmallTest -public class HSIconFileElementTest { +public class HSIconFileElementTest extends WifiBaseTest { private static final String TEST_ICON_TYPE = "png"; private static final byte[] TEST_ICON_DATA = new byte[8]; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElementTest.java index de89ff9800..dfb5abec24 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElementTest.java @@ -22,6 +22,8 @@ import android.net.wifi.WifiSsid; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -37,7 +39,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement}. */ @SmallTest -public class HSOsuProvidersElementTest { +public class HSOsuProvidersElementTest extends WifiBaseTest { private static final byte[] TEST_OSU_SSID_BYTES = "Test SSID".getBytes(StandardCharsets.UTF_8); private static final WifiSsid TEST_OSU_SSID = WifiSsid.createFromByteArray(TEST_OSU_SSID_BYTES); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java index bebfc5adfb..e4b65f359d 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -30,7 +32,7 @@ import java.nio.ByteOrder; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement}. */ @SmallTest -public class HSWanMetricsElementTest { +public class HSWanMetricsElementTest extends WifiBaseTest { 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; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java index e495b8417f..bf9e230451 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -34,7 +36,7 @@ import java.util.Locale; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.I18Name}. */ @SmallTest -public class I18NameTest { +public class I18NameTest extends WifiBaseTest { 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"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java index 9c2862708f..2250871be0 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -29,7 +31,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement}. */ @SmallTest -public class IPAddressTypeAvailabilityElementTest { +public class IPAddressTypeAvailabilityElementTest extends WifiBaseTest { private static final int TEST_IPV4_AVAILABILITY = IPAddressTypeAvailabilityElement.IPV4_PUBLIC; private static final int TEST_IPV6_AVAILABILITY = diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTest.java index c44ad0bf02..3580a745ce 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertNotEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.nio.BufferUnderflowException; @@ -30,7 +32,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.IconInfo}. */ @SmallTest -public class IconInfoTest { +public class IconInfoTest extends WifiBaseTest { private static final int TEST_WIDTH = 1111; private static final int TEST_HEIGHT = 2222; private static final String TEST_LANGUAGE = "language"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTest.java index aa342d4af7..4839b3d63b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -30,7 +32,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.NAIRealmData}. */ @SmallTest -public class NAIRealmDataTest { +public class NAIRealmDataTest extends WifiBaseTest { /** * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer. * diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmElementTest.java index 553f0ee833..6e0c87a3cd 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmElementTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.nio.ByteBuffer; @@ -32,7 +34,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.NAIRealmElement}. */ @SmallTest -public class NAIRealmElementTest { +public class NAIRealmElementTest extends WifiBaseTest { /** * Helper function for returning a ByteBuffer containing raw bytes for NAI Realm Element * with specified number of NAI Realm Data. diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java index 2855db9d77..7dea30be04 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertNotNull; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -34,7 +36,7 @@ import java.util.Locale; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.OsuProviderInfo}. */ @SmallTest -public class OsuProviderInfoTest { +public class OsuProviderInfoTest extends WifiBaseTest { /** * Verify that BufferUnderflowException will be thrown when parsing an empty buffer. * @throws Exception diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java index fc0942a7e3..ba6a79f7ef 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.nio.BufferUnderflowException; @@ -30,7 +32,7 @@ import java.nio.ByteOrder; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.ProtocolPortTuple}. */ @SmallTest -public class ProtocolPortTupleTest { +public class ProtocolPortTupleTest extends WifiBaseTest { private static final int TEST_PROTOCOL = 1; private static final int TEST_PORT = 2; private static final int TEST_STATUS = ProtocolPortTuple.PROTO_STATUS_CLOSED; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java index 3032210eb7..dd78f33339 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.nio.ByteBuffer; @@ -28,7 +30,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.RawByteElement}. */ @SmallTest -public class RawByteElementTest { +public class RawByteElementTest extends WifiBaseTest { private static final Constants.ANQPElementType TEST_ELEMENT_ID = Constants.ANQPElementType.HSOSUProviders; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java index dad7c2bc4d..2df578a409 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java @@ -23,6 +23,8 @@ import android.util.Pair; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -37,7 +39,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement}. */ @SmallTest -public class RoamingConsortiumElementTest { +public class RoamingConsortiumElementTest extends WifiBaseTest { // 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); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElementTest.java index 4add837699..8a036e0680 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElementTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -34,7 +36,7 @@ import java.util.List; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement}. */ @SmallTest -public class ThreeGPPNetworkElementTest { +public class ThreeGPPNetworkElementTest extends WifiBaseTest { private static final byte[][] TEST_NETWORK1_PLMN_BYTES = new byte[][] { new byte[] {0x21, 0x63, 0x54}, new byte[] {0x43, (byte) 0x85, 0x76} }; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java index 006ebac36e..3bfcd69ff0 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -38,7 +40,7 @@ import java.util.Locale; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.VenueNameElement}. */ @SmallTest -public class VenueNameElementTest { +public class VenueNameElementTest extends WifiBaseTest { 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"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/CredentialTypeTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/CredentialTypeTest.java index 160029b639..d130571b76 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/CredentialTypeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/CredentialTypeTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -30,7 +32,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.CredentialType}. */ @SmallTest -public class CredentialTypeTest { +public class CredentialTypeTest extends WifiBaseTest { private static final int TEST_TYPE = CredentialType.CREDENTIAL_TYPE_USIM; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/EAPMethodTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/EAPMethodTest.java index 8ddd9a8f17..69c20d5d5a 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/EAPMethodTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/EAPMethodTest.java @@ -22,6 +22,8 @@ import android.net.wifi.EAPConstants; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -36,7 +38,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.EAPMethod}. */ @SmallTest -public class EAPMethodTest { +public class EAPMethodTest extends WifiBaseTest { /** * Setup basic test data - contained multiple parameters of the same type. */ diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethodTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethodTest.java index 1c112dfaa3..a2c92fd9a9 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethodTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethodTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -30,7 +32,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.ExpandedEAPMethod}. */ @SmallTest -public class ExpandedEAPMethodTest { +public class ExpandedEAPMethodTest extends WifiBaseTest { private static final int TEST_VENDOR_ID = 0x123456; private static final long TEST_VENDOR_TYPE = 0x23456523; private static final byte[] TEST_DATA_BYTES = diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAPTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAPTest.java index d7087d94da..6a2bc79a30 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAPTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAPTest.java @@ -22,6 +22,8 @@ import android.net.wifi.EAPConstants; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -32,7 +34,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.InnerAuthEAP}. */ @SmallTest -public class InnerAuthEAPTest { +public class InnerAuthEAPTest extends WifiBaseTest { private static final int TEST_EAP_METHOD_ID = EAPConstants.EAP_TTLS; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuthTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuthTest.java index 3a4b6ed3a4..c3101f7e43 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuthTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuthTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.net.ProtocolException; @@ -30,7 +32,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth}. */ @SmallTest -public class NonEAPInnerAuthTest { +public class NonEAPInnerAuthTest extends WifiBaseTest { private static final int TEST_AUTH_TYPE = NonEAPInnerAuth.AUTH_TYPE_MSCHAP; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuthTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuthTest.java index 491077e3b6..3e167f8474 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuthTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuthTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.nio.BufferUnderflowException; @@ -29,7 +31,7 @@ import java.nio.ByteBuffer; * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.VendorSpecificAuth}. */ @SmallTest -public class VendorSpecificAuthTest { +public class VendorSpecificAuthTest extends WifiBaseTest { private static final byte[] TEST_DATA = new byte[] {0x12, 0x34, 0x45, 0x56}; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevDetailMoTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevDetailMoTest.java index f54e360d0c..af3fa0219a 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevDetailMoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevDetailMoTest.java @@ -30,6 +30,7 @@ import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.SystemInfo; import org.junit.Before; @@ -41,7 +42,7 @@ import org.mockito.Mock; * TODO(b/80300806): change the test to verify the XML in terms of the structure of XML. */ @SmallTest -public class DevDetailMoTest { +public class DevDetailMoTest extends WifiBaseTest { private static final String TEST_DEV_ID = "12312341"; private static final String TEST_MANUFACTURER = Build.MANUFACTURER; private static final String TEST_HW_VERSION = "Test HW 1.0"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevInfoMoTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevInfoMoTest.java index d0a538ed57..aaa4e39d4b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevInfoMoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/DevInfoMoTest.java @@ -24,6 +24,7 @@ import android.os.Build; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.SystemInfo; import org.junit.Test; @@ -33,7 +34,7 @@ import org.junit.Test; * TODO(b/80300806): change the test to verify the XML in terms of the structure of XML. */ @SmallTest -public class DevInfoMoTest { +public class DevInfoMoTest extends WifiBaseTest { private static final String TEST_DEV_ID = "12312341"; private static final String TEST_MANUFACTURER = Build.MANUFACTURER; private static final String TEST_MODEL = Build.MODEL; @@ -61,4 +62,4 @@ public class DevInfoMoTest { TEST_MODEL, MoSerializer.DM_VERSION, TEST_LANGUAGE); assertEquals(expected, DevInfoMo.serializeToXml(systemInfo)); } -}
\ No newline at end of file +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/MoSerializerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/MoSerializerTest.java index 2c28ed22ce..213dd74c7d 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/MoSerializerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/omadm/MoSerializerTest.java @@ -22,6 +22,8 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.w3c.dom.Document; @@ -35,7 +37,7 @@ import java.util.List; * Unit tests for {@link MoSerializer}. */ @SmallTest -public class MoSerializerTest { +public class MoSerializerTest extends WifiBaseTest { MoSerializer mMoSerializer; Document mDocument; static final String TEST_NODE = "test_node"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/ExchangeCompleteMessageTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/ExchangeCompleteMessageTest.java index 933a20602b..4b2739be8d 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/ExchangeCompleteMessageTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/ExchangeCompleteMessageTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertNull; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import org.ksoap2.serialization.SoapObject; @@ -28,7 +30,7 @@ import org.ksoap2.serialization.SoapObject; * Unit tests for {@link ExchangeCompleteMessage}. */ @SmallTest -public class ExchangeCompleteMessageTest { +public class ExchangeCompleteMessageTest extends WifiBaseTest { private static final String TEST_STATUS = "OK"; private static final String TEST_SESSION_ID = "D215D696517BA138F1D28442DF0F4E07"; private static final String TEST_VERSION = "1.0"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsServiceConnectionTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsServiceConnectionTest.java index 3d5dae847f..ee813a6b63 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsServiceConnectionTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsServiceConnectionTest.java @@ -27,6 +27,8 @@ import android.net.Network; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.ksoap2.HeaderProperty; @@ -48,7 +50,7 @@ import javax.net.ssl.HttpsURLConnection; * Unit tests for {@link HttpsServiceConnection}. */ @SmallTest -public class HttpsServiceConnectionTest { +public class HttpsServiceConnectionTest extends WifiBaseTest { private static final String TEST_URL = "https://127.0.0.1:12345/index.htm"; private HttpsServiceConnection mHttpsServiceConnection; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsTransportTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsTransportTest.java index 6975765d82..d0aff449ba 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsTransportTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/HttpsTransportTest.java @@ -25,6 +25,8 @@ import android.net.Network; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -38,7 +40,7 @@ import javax.net.ssl.HttpsURLConnection; * Unit tests for {@link HttpsTransport}. */ @SmallTest -public class HttpsTransportTest { +public class HttpsTransportTest extends WifiBaseTest { private static final String TEST_URL = "https://127.0.0.1:12345/index.htm"; private URL mUrl; private HttpsTransport mHttpsTransport; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataMessageTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataMessageTest.java index 7fe03e124e..841dcd6f2b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataMessageTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataMessageTest.java @@ -31,6 +31,7 @@ import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.SystemInfo; import com.android.server.wifi.hotspot2.omadm.DevDetailMo; import com.android.server.wifi.hotspot2.omadm.DevInfoMo; @@ -49,7 +50,7 @@ import org.mockito.Mock; * Unit tests for {@link PostDevDataMessage}. */ @SmallTest -public class PostDevDataMessageTest { +public class PostDevDataMessageTest extends WifiBaseTest { private static final String TEST_DEV_ID = "12312341"; private static final String TEST_MANUFACTURER = Build.MANUFACTURER; private static final String TEST_MODEL = Build.MODEL; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataResponseTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataResponseTest.java index 0b48a73ef3..9e1fb2983c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataResponseTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/PostDevDataResponseTest.java @@ -22,6 +22,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.soap.command.SppCommand; import org.junit.Before; @@ -33,7 +34,7 @@ import org.ksoap2.serialization.SoapObject; * Unit tests for {@link PostDevDataResponse}. */ @SmallTest -public class PostDevDataResponseTest { +public class PostDevDataResponseTest extends WifiBaseTest { private static final String EXEC = "exec"; private static final String BROWSER_COMMAND = "launchBrowserToURI"; private static final String TEST_URL = "https://127.0.0.1:12345/index.htm"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java index d61a59d6f9..aa4e188730 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java @@ -29,6 +29,8 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -42,7 +44,7 @@ import fi.iki.elonen.NanoHTTPD; * Unit tests for {@link RedirectListener}. */ @SmallTest -public class RedirectListenerTest { +public class RedirectListenerTest extends WifiBaseTest { private static final int TEST_PORT = 1010; private RedirectListenerSpy mRedirectListener; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SoapParserTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SoapParserTest.java index 2445caba5c..c52a84abd4 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SoapParserTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SoapParserTest.java @@ -22,6 +22,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.ksoap2.serialization.PropertyInfo; @@ -31,7 +33,7 @@ import org.ksoap2.serialization.SoapObject; * Unit tests for {@link SoapParser}. */ @SmallTest -public class SoapParserTest { +public class SoapParserTest extends WifiBaseTest { private static final String EXEC = "exec"; private static final String BROWSER_COMMAND = "launchBrowserToURI"; private static final String TEST_URL = "https://127.0.0.1:12345/index.htm"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SppResponseMessageTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SppResponseMessageTest.java index 4169868f1d..823cc9d602 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SppResponseMessageTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/SppResponseMessageTest.java @@ -22,6 +22,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.ksoap2.serialization.SoapObject; @@ -33,7 +35,7 @@ import java.util.Map; * Unit tests for {@link SppResponseMessage}. */ @SmallTest -public class SppResponseMessageTest { +public class SppResponseMessageTest extends WifiBaseTest { private static final String TEST_STATUS = "OK"; private static final String TEST_ERROR_STATUS = "Error occurred"; private static final String TEST_SESSION_ID = "D215D696517BA138F1D28442DF0F4E07"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/UpdateResponseMessageTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/UpdateResponseMessageTest.java index 9098c2297b..881ed9c943 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/UpdateResponseMessageTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/UpdateResponseMessageTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertNotNull; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import org.ksoap2.SoapEnvelope; import org.ksoap2.serialization.SoapObject; @@ -30,7 +32,7 @@ import org.ksoap2.serialization.SoapSerializationEnvelope; * Unit tests for {@link UpdateResponseMessage}. */ @SmallTest -public class UpdateResponseMessageTest { +public class UpdateResponseMessageTest extends WifiBaseTest { private static final String TEST_SESSION_ID = "123456"; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/BrowserUriTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/BrowserUriTest.java index 2fe5e752f0..e5691e7d88 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/BrowserUriTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/BrowserUriTest.java @@ -22,6 +22,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.ksoap2.serialization.PropertyInfo; @@ -30,7 +32,7 @@ import org.ksoap2.serialization.PropertyInfo; * Unit tests for {@link BrowserUri}. */ @SmallTest -public class BrowserUriTest { +public class BrowserUriTest extends WifiBaseTest { private static final String BROWSER_COMMAND = "launchBrowserToURI"; private static final String TEST_URL = "https://127.0.0.1:12345/index.htm"; private BrowserUri mBrowserUri; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/PpsMoDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/PpsMoDataTest.java index cc56b06ef2..28642661ef 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/PpsMoDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/PpsMoDataTest.java @@ -26,6 +26,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.ksoap2.serialization.PropertyInfo; @@ -35,7 +37,7 @@ import org.ksoap2.serialization.SoapPrimitive; * Unit tests for {@link PpsMoData}. */ @SmallTest -public class PpsMoDataTest { +public class PpsMoDataTest extends WifiBaseTest { private static final String TEST_PPS_MO_XML = "<MgmtTree>test</MgmtTree>"; private static final String TEST_TREE_URI = "testTreeURI"; private static final String TEST_MO_URN = "testMoURN"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/SppCommandTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/SppCommandTest.java index 52d4615ad1..1f00e2c520 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/SppCommandTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/command/SppCommandTest.java @@ -22,6 +22,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.ksoap2.serialization.PropertyInfo; @@ -31,7 +33,7 @@ import org.ksoap2.serialization.SoapObject; * Unit tests for {@link SppCommand}. */ @SmallTest -public class SppCommandTest { +public class SppCommandTest extends WifiBaseTest { private static final String EXEC = "exec"; private static final String BROWSER_COMMAND = "launchBrowserToURI"; private static final String GET_CERT_COMMAND = "getCertificate"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java index 807352cf42..fafb632227 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java @@ -36,6 +36,7 @@ import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus; import com.android.server.wifi.util.NativeUtil; @@ -53,7 +54,7 @@ import java.util.List; * Unit tests for SupplicantP2pIfaceCallback */ @SmallTest -public class SupplicantP2pIfaceCallbackTest { +public class SupplicantP2pIfaceCallbackTest extends WifiBaseTest { private static final String TAG = "SupplicantP2pIfaceCallbackTest"; private String mIface = "test_p2p0"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java index 8c3328eff0..c642daf0cb 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java @@ -51,6 +51,7 @@ import android.text.TextUtils; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.util.NativeUtil; import org.junit.Assert.*; @@ -71,7 +72,7 @@ import java.util.Map; * Unit tests for SupplicantP2pIfaceHal */ @SmallTest -public class SupplicantP2pIfaceHalTest { +public class SupplicantP2pIfaceHalTest extends WifiBaseTest { private static final String TAG = "SupplicantP2pIfaceHalTest"; private SupplicantP2pIfaceHal mDut; private @Mock IServiceManager mServiceManagerMock; diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMetricsTest.java index 7d4796d212..bc69ec68a2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMetricsTest.java @@ -24,6 +24,7 @@ import android.net.wifi.p2p.WifiP2pGroupList; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.nano.WifiMetricsProto.GroupEvent; import com.android.server.wifi.nano.WifiMetricsProto.P2pConnectionEvent; import com.android.server.wifi.nano.WifiMetricsProto.WifiP2pStats; @@ -37,7 +38,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiP2pMetrics}. */ @SmallTest -public class WifiP2pMetricsTest { +public class WifiP2pMetricsTest extends WifiBaseTest { @Mock Clock mClock; WifiP2pMetrics mWifiP2pMetrics; diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMonitorTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMonitorTest.java index 68aceb37e1..bced05d007 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMonitorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMonitorTest.java @@ -27,6 +27,7 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiInjector; import org.junit.Before; @@ -37,7 +38,7 @@ import org.mockito.ArgumentCaptor; * Unit tests for {@link com.android.server.wifi.WifiP2pMonitor}. */ @SmallTest -public class WifiP2pMonitorTest { +public class WifiP2pMonitorTest extends WifiBaseTest { private static final String P2P_IFACE_NAME = "p2p0"; private static final String SECOND_P2P_IFACE_NAME = "p2p1"; private WifiP2pMonitor mWifiP2pMonitor; diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java index 962acd01e7..eb48b71c75 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java @@ -41,6 +41,7 @@ import com.android.server.wifi.HalDeviceManager.InterfaceAvailableForRequestList import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener; import com.android.server.wifi.HalDeviceManager.ManagerStatusListener; import com.android.server.wifi.PropertyService; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiVendorHal; import org.junit.Before; @@ -54,7 +55,7 @@ import org.mockito.MockitoAnnotations; * {@link com.android.server.wifi.WifiP2pNative}. */ @SmallTest -public class WifiP2pNativeInterfaceManagementTest { +public class WifiP2pNativeInterfaceManagementTest extends WifiBaseTest { private static final String P2P_IFACE_NAME = "p2p0"; private static final String P2P_INTERFACE_PROPERTY = "wifi.direct.interface"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java index 4fda65e38e..045b1752f3 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java @@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.server.wifi.HalDeviceManager; import com.android.server.wifi.PropertyService; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiVendorHal; import org.junit.Before; @@ -55,7 +56,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.WifiP2pMonitor}. */ @SmallTest -public class WifiP2pNativeTest { +public class WifiP2pNativeTest extends WifiBaseTest { private static final String TEST_DEVICE_NAME = "Android_HelloWorld"; private static final String TEST_IFACE = "p2p-p2p0-1"; diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java index 15d9ef9157..8127ffe478 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java @@ -78,6 +78,7 @@ import com.android.internal.R; import com.android.server.wifi.FakeWifiLog; import com.android.server.wifi.FrameworkFacade; import com.android.server.wifi.HalDeviceManager; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.nano.WifiMetricsProto.P2pConnectionEvent; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -97,7 +98,7 @@ import java.util.List; * Unit test harness for WifiP2pServiceImpl. */ @SmallTest -public class WifiP2pServiceImplTest { +public class WifiP2pServiceImplTest extends WifiBaseTest { private static final String TAG = "WifiP2pServiceImplTest"; private static final String IFACE_NAME_P2P = "mockP2p0"; private static final long STATE_CHANGE_WAITING_TIME = 1000; diff --git a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttMetricsTest.java index a319539c53..891fb40030 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttMetricsTest.java @@ -30,6 +30,7 @@ import android.util.Log; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.nano.WifiMetricsProto; import org.junit.Before; @@ -48,7 +49,7 @@ import java.util.List; * Unit test harness for RttMetrics */ @SmallTest -public class RttMetricsTest { +public class RttMetricsTest extends WifiBaseTest { private RttMetrics mDut; @Mock diff --git a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java index d2f22da6a9..f6562fae77 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java @@ -46,6 +46,7 @@ import android.net.wifi.rtt.RangingRequest; import androidx.test.filters.SmallTest; import com.android.server.wifi.HalDeviceManager; +import com.android.server.wifi.WifiBaseTest; import org.hamcrest.core.IsNull; import org.junit.Before; @@ -63,7 +64,7 @@ import java.util.List; * Unit test harness for the RttNative class. */ @SmallTest -public class RttNativeTest { +public class RttNativeTest extends WifiBaseTest { private RttNative mDut; private WifiStatus mStatusSuccess; diff --git a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java index cd61aac08c..1156aa863b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java @@ -75,6 +75,7 @@ import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; import com.android.server.wifi.FrameworkFacade; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.nano.WifiMetricsProto; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -98,7 +99,7 @@ import java.util.Set; * Unit test harness for the RttServiceImpl class. */ @SmallTest -public class RttServiceImplTest { +public class RttServiceImplTest extends WifiBaseTest { private static final long BACKGROUND_PROCESS_EXEC_GAP_MS = 10 * 60 * 1000; // 10 minutes. diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java index f5e777e202..39ecb6f6eb 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java @@ -32,6 +32,7 @@ import android.util.ArraySet; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiNative; import com.android.server.wifi.WifiNative.BucketSettings; import com.android.server.wifi.scanner.KnownBandsChannelHelper.KnownBandsChannelCollection; @@ -50,7 +51,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.scanner.BackgroundScanScheduler}. */ @SmallTest -public class BackgroundScanSchedulerTest { +public class BackgroundScanSchedulerTest extends WifiBaseTest { private static final int DEFAULT_MAX_BUCKETS = 9; private static final int DEFAULT_MAX_CHANNELS_PER_BUCKET = 23; diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java index 7e02507395..9b24b14a4f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java @@ -39,6 +39,7 @@ import com.android.server.wifi.MockResources; import com.android.server.wifi.MockWifiMonitor; import com.android.server.wifi.ScanDetail; import com.android.server.wifi.ScanResults; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiMonitor; import com.android.server.wifi.WifiNative; import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; @@ -60,7 +61,7 @@ import java.util.Set; * {@link com.android.server.wifi.scanner.WifiScannerImpl}. */ @SmallTest -public abstract class BaseWifiScannerImplTest { +public abstract class BaseWifiScannerImplTest extends WifiBaseTest { protected static final String IFACE_NAME = "a_test_interface_name"; @Mock Context mContext; TestAlarmManager mAlarmManager; diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/ChannelHelperTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/ChannelHelperTest.java index 74674b966b..5ad6c32f5e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/ChannelHelperTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/ChannelHelperTest.java @@ -30,6 +30,7 @@ import android.net.wifi.WifiScanner; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiNative; import org.junit.Before; @@ -41,14 +42,14 @@ import org.junit.runner.RunWith; * Unit tests for {@link com.android.server.wifi.scanner.ChannelHelper}. */ @RunWith(Enclosed.class) // WARNING: tests cannot be declared in the outer class -public class ChannelHelperTest { +public class ChannelHelperTest extends WifiBaseTest { /** * Unit tests for * {@link com.android.server.wifi.scanner.ChannelHelper#toString}. */ @SmallTest - public static class ToStringTest { + public static class ToStringTest extends WifiBaseTest { /** * Compute a string representing the channels in a ScanSettings with a band set. */ @@ -117,7 +118,7 @@ public class ChannelHelperTest { * {@link com.android.server.wifi.scanner.ChannelHelper.ChannelCollection}. */ @SmallTest - public static class ChannelCollectionTest { + public static class ChannelCollectionTest extends WifiBaseTest { ChannelHelper.ChannelCollection mChannelCollection; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java index bc70f65b85..2fadf0355e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java @@ -31,6 +31,7 @@ import android.net.wifi.WifiScanner; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiNative; import org.junit.Before; @@ -51,13 +52,14 @@ public class KnownBandsChannelHelperTest { private static final int[] CHANNELS_24_GHZ = new int[]{2400, 2450}; private static final int[] CHANNELS_5_GHZ = new int[]{5150, 5175}; private static final int[] CHANNELS_DFS = new int[]{5600, 5650, 5660}; + private static final int[] CHANNELS_DFS_OTHER = new int[]{5600, 5650, 5660, 5680}; /** * Unit tests for * {@link com.android.server.wifi.scanner.KnownBandsChannelHelper.estimateScanDuration}. */ @SmallTest - public static class EstimateScanDurationTest { + public static class EstimateScanDurationTest extends WifiBaseTest { KnownBandsChannelHelper mChannelHelper; /** @@ -102,7 +104,7 @@ public class KnownBandsChannelHelperTest { * {@link com.android.server.wifi.scanner.KnownBandsChannelHelper.getAvailableScanChannels}. */ @SmallTest - public static class GetAvailableScanChannelsTest { + public static class GetAvailableScanChannelsTest extends WifiBaseTest { KnownBandsChannelHelper mChannelHelper; /** @@ -185,7 +187,7 @@ public class KnownBandsChannelHelperTest { * {@link com.android.server.wifi.scanner.KnownBandsChannelHelper.settingsContainChannel}. */ @SmallTest - public static class SettingsContainChannelTest { + public static class SettingsContainChannelTest extends WifiBaseTest { KnownBandsChannelHelper mChannelHelper; /** @@ -257,10 +259,49 @@ public class KnownBandsChannelHelperTest { /** * Unit tests for + * {@link com.android.server.wifi.scanner.KnownBandsChannelHelper#equals(ChannelHelper)}. + */ + @SmallTest + public static class EqualsTest extends WifiBaseTest { + /** + * Creates 2 channel helper instances which are equal. + */ + @Test + public void channelHelpersAreSatisfiedBySame() { + KnownBandsChannelHelper channelHelper0 = new PresetKnownBandsChannelHelper( + CHANNELS_24_GHZ, + CHANNELS_5_GHZ, + CHANNELS_DFS); + KnownBandsChannelHelper channelHelper1 = new PresetKnownBandsChannelHelper( + CHANNELS_24_GHZ, + CHANNELS_5_GHZ, + CHANNELS_DFS); + assertTrue(channelHelper0.satisfies(channelHelper1)); + } + + /** + * Creates 2 channel helper instances which are equal. + */ + @Test + public void channelHelpersAreNotSatisfiedByDifferent() { + KnownBandsChannelHelper channelHelper0 = new PresetKnownBandsChannelHelper( + CHANNELS_24_GHZ, + CHANNELS_5_GHZ, + CHANNELS_DFS); + KnownBandsChannelHelper channelHelper1 = new PresetKnownBandsChannelHelper( + CHANNELS_24_GHZ, + CHANNELS_5_GHZ, + CHANNELS_DFS_OTHER); + assertFalse(channelHelper0.satisfies(channelHelper1)); + } + } + + /** + * Unit tests for * {@link com.android.server.wifi.scanner.KnownBandsChannelHelper.KnownBandsChannelCollection}. */ @SmallTest - public static class KnownBandsChannelCollectionTest { + public static class KnownBandsChannelCollectionTest extends WifiBaseTest { ChannelHelper.ChannelCollection mChannelCollection; /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/ScanScheduleUtilFilterTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/ScanScheduleUtilFilterTest.java index e08829f8f1..4bf2766ed3 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/ScanScheduleUtilFilterTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/ScanScheduleUtilFilterTest.java @@ -33,6 +33,8 @@ import android.net.wifi.WifiScanner.ScanSettings; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; @@ -41,7 +43,7 @@ import org.junit.Test; * {@link com.android.server.wifi.scanner.ScanScheduleUtil}. */ @SmallTest -public class ScanScheduleUtilFilterTest { +public class ScanScheduleUtilFilterTest extends WifiBaseTest { private ChannelHelper mChannelHelper; diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java index c2c974499d..ea1fb51d50 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java @@ -17,6 +17,8 @@ package com.android.server.wifi.scanner; import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.wifi.WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA; import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder; import static com.android.server.wifi.ScanTestUtil.assertNativePnoSettingsEquals; @@ -51,13 +53,15 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.app.test.TestAlarmManager; import android.content.BroadcastReceiver; import android.content.Context; import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; +import android.net.wifi.WifiStackClient; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -65,6 +69,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.WorkSource; import android.os.test.TestLooper; +import android.util.ArraySet; import android.util.Pair; import androidx.test.filters.SmallTest; @@ -73,12 +78,12 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import com.android.internal.util.test.BidirectionalAsyncChannel; -import com.android.server.wifi.CellularLinkLayerStatsCollector; import com.android.server.wifi.Clock; import com.android.server.wifi.DppMetrics; import com.android.server.wifi.FakeWifiLog; import com.android.server.wifi.FrameworkFacade; import com.android.server.wifi.ScanResults; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiMetrics; import com.android.server.wifi.WifiNative; @@ -112,15 +117,20 @@ import java.util.regex.Pattern; * Unit tests for {@link com.android.server.wifi.scanner.WifiScanningServiceImpl}. */ @SmallTest -public class WifiScanningServiceTest { +public class WifiScanningServiceTest extends WifiBaseTest { public static final String TAG = "WifiScanningServiceTest"; private static final int TEST_MAX_SCAN_BUCKETS_IN_CAPABILITIES = 8; private static final String TEST_PACKAGE_NAME = "com.test.123"; + private static final String TEST_IFACE_NAME_0 = "wlan0"; + private static final String TEST_IFACE_NAME_1 = "wlan1"; + private static final WifiScanner.ScanData DUMMY_SCAN_DATA = + new WifiScanner.ScanData(0, 0, new ScanResult[0]); @Mock Context mContext; TestAlarmManager mAlarmManager; - @Mock WifiScannerImpl mWifiScannerImpl; + @Mock WifiScannerImpl mWifiScannerImpl0; + @Mock WifiScannerImpl mWifiScannerImpl1; @Mock WifiScannerImpl.WifiScannerImplFactory mWifiScannerImplFactory; @Mock IBatteryStats mBatteryStats; @Mock WifiInjector mWifiInjector; @@ -129,11 +139,13 @@ public class WifiScanningServiceTest { @Spy FakeWifiLog mLog; @Mock WifiPermissionsUtil mWifiPermissionsUtil; @Mock DppMetrics mDppMetrics; + @Mock WifiNative mWifiNative; + ChannelHelper mChannelHelper0; + ChannelHelper mChannelHelper1; WifiMetrics mWifiMetrics; TestLooper mLooper; WifiScanningServiceImpl mWifiScanningServiceImpl; @Mock WifiP2pMetrics mWifiP2pMetrics; - @Mock CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector; @Before public void setUp() throws Exception { @@ -145,19 +157,28 @@ public class WifiScanningServiceTest { when(mWifiInjector.getWifiPermissionsUtil()) .thenReturn(mWifiPermissionsUtil); - ChannelHelper channelHelper = new PresetKnownBandsChannelHelper( + mChannelHelper0 = new PresetKnownBandsChannelHelper( new int[]{2400, 2450}, new int[]{5150, 5175}, new int[]{5600, 5650, 5660}); + mChannelHelper1 = new PresetKnownBandsChannelHelper( + new int[]{2400, 2450}, + new int[]{5150, 5175}, + new int[]{5600, 5660, 5680}); // 5650 is missing from channelHelper0 mLooper = new TestLooper(); mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, mLooper.getLooper(), - new WifiAwareMetrics(mClock), new RttMetrics(mClock), new WifiPowerMetrics(), - mWifiP2pMetrics, mDppMetrics, mCellularLinkLayerStatsCollector); + new WifiAwareMetrics(mClock), new RttMetrics(mClock), + new WifiPowerMetrics(mBatteryStats), + mWifiP2pMetrics, mDppMetrics); when(mWifiScannerImplFactory - .create(any(), any(), any())) - .thenReturn(mWifiScannerImpl); - when(mWifiScannerImpl.getChannelHelper()).thenReturn(channelHelper); + .create(any(), any(), any(), eq(TEST_IFACE_NAME_0))) + .thenReturn(mWifiScannerImpl0); + when(mWifiScannerImpl0.getChannelHelper()).thenReturn(mChannelHelper0); + when(mWifiScannerImplFactory + .create(any(), any(), any(), eq(TEST_IFACE_NAME_1))) + .thenReturn(mWifiScannerImpl1); + when(mWifiScannerImpl1.getChannelHelper()).thenReturn(mChannelHelper1); when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); when(mWifiInjector.makeLog(anyString())).thenReturn(mLog); WifiAsyncChannel mWifiAsyncChannel = new WifiAsyncChannel("ScanningServiceTest"); @@ -165,6 +186,12 @@ public class WifiScanningServiceTest { when(mFrameworkFacade.makeWifiAsyncChannel(anyString())).thenReturn(mWifiAsyncChannel); when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade); when(mWifiInjector.getClock()).thenReturn(mClock); + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0))); + when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative); + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) + .thenReturn(PERMISSION_GRANTED); mWifiScanningServiceImpl = new WifiScanningServiceImpl(mContext, mLooper.getLooper(), mWifiScannerImplFactory, mBatteryStats, mWifiInjector); } @@ -381,11 +408,16 @@ public class WifiScanningServiceTest { private WifiNative.ScanEventHandler verifyStartSingleScan(InOrder order, WifiNative.ScanSettings expected) { + return verifyStartSingleScanForImpl(mWifiScannerImpl0, order, expected); + } + + private WifiNative.ScanEventHandler verifyStartSingleScanForImpl( + WifiScannerImpl wifiScannerImpl, InOrder order, WifiNative.ScanSettings expected) { ArgumentCaptor<WifiNative.ScanSettings> scanSettingsCaptor = ArgumentCaptor.forClass(WifiNative.ScanSettings.class); ArgumentCaptor<WifiNative.ScanEventHandler> scanEventHandlerCaptor = ArgumentCaptor.forClass(WifiNative.ScanEventHandler.class); - order.verify(mWifiScannerImpl).startSingleScan(scanSettingsCaptor.capture(), + order.verify(wifiScannerImpl).startSingleScan(scanSettingsCaptor.capture(), scanEventHandlerCaptor.capture()); assertNativeScanSettingsEquals(expected, scanSettingsCaptor.getValue()); return scanEventHandlerCaptor.getValue(); @@ -397,7 +429,7 @@ public class WifiScanningServiceTest { ArgumentCaptor.forClass(WifiNative.ScanSettings.class); ArgumentCaptor<WifiNative.ScanEventHandler> scanEventHandlerCaptor = ArgumentCaptor.forClass(WifiNative.ScanEventHandler.class); - order.verify(mWifiScannerImpl).startBatchedScan(scanSettingsCaptor.capture(), + order.verify(mWifiScannerImpl0).startBatchedScan(scanSettingsCaptor.capture(), scanEventHandlerCaptor.capture()); assertNativeScanSettingsEquals(expected, scanSettingsCaptor.getValue()); return scanEventHandlerCaptor.getValue(); @@ -411,7 +443,7 @@ public class WifiScanningServiceTest { } private void setupAndLoadDriver(int max_scan_buckets) { - when(mWifiScannerImpl.getScanCapabilities(any(WifiNative.ScanCapabilities.class))) + when(mWifiScannerImpl0.getScanCapabilities(any(WifiNative.ScanCapabilities.class))) .thenAnswer(new AnswerWithArguments() { public boolean answer(WifiNative.ScanCapabilities capabilities) { capabilities.max_scan_cache_size = Integer.MAX_VALUE; @@ -457,7 +489,7 @@ public class WifiScanningServiceTest { @Test public void construct() throws Exception { - verifyNoMoreInteractions(mWifiScannerImpl, mWifiScannerImpl, + verifyNoMoreInteractions(mWifiScannerImpl0, mWifiScannerImpl0, mWifiScannerImplFactory, mBatteryStats); dumpService(); // make sure this succeeds } @@ -492,12 +524,12 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); verify(mWifiScannerImplFactory, times(1)) - .create(any(), any(), any()); + .create(any(), any(), any(), eq(TEST_IFACE_NAME_0)); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); InOrder order = inOrder(handler); - when(mWifiScannerImpl.startBatchedScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startBatchedScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendBackgroundScanRequest(controlChannel, 192, generateValidScanSettings(), null); mLooper.dispatchAll(); @@ -519,7 +551,7 @@ public class WifiScanningServiceTest { // Ensure we didn't create scanner instance twice. verify(mWifiScannerImplFactory, times(1)) - .create(any(), any(), any()); + .create(any(), any(), any(), any()); } @Test @@ -553,7 +585,7 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage(Message.obtain(null, Protocol.BASE_WIFI_MANAGER)); mLooper.dispatchAll(); verifyFailedResponse(order, handler, 0, WifiScanner.REASON_INVALID_REQUEST, @@ -578,7 +610,7 @@ public class WifiScanningServiceTest { @Test public void rejectBackgroundScanRequestWhenScannerImplCreateFails() throws Exception { // Fail scanner impl creation. - when(mWifiScannerImplFactory.create(any(), any(), any())).thenReturn(null); + when(mWifiScannerImplFactory.create(any(), any(), any(), any())).thenReturn(null); startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); @@ -592,7 +624,14 @@ public class WifiScanningServiceTest { } private void doSuccessfulSingleScan(WifiScanner.ScanSettings requestSettings, - WifiNative.ScanSettings nativeSettings, ScanResults results) throws RemoteException { + WifiNative.ScanSettings nativeSettings, @NonNull ScanResults resultsForImpl0) + throws RemoteException { + doSuccessfulSingleScanOnImpls(requestSettings, nativeSettings, resultsForImpl0, null); + } + + private void doSuccessfulSingleScanOnImpls(WifiScanner.ScanSettings requestSettings, + WifiNative.ScanSettings nativeSettings, @NonNull ScanResults resultsForImpl0, + @Nullable ScanResults resultsForImpl1) throws RemoteException { int requestId = 12; WorkSource workSource = new WorkSource(2292); startServiceAndLoadDriver(); @@ -600,30 +639,50 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); + if (resultsForImpl1 != null) { + when(mWifiScannerImpl1.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + } sendSingleScanRequest(controlChannel, requestId, requestSettings, workSource); mLooper.dispatchAll(); - WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order, nativeSettings); + WifiNative.ScanEventHandler eventHandler0 = + verifyStartSingleScanForImpl(mWifiScannerImpl0, order, nativeSettings); + WifiNative.ScanEventHandler eventHandler1 = null; + if (resultsForImpl1 != null) { + eventHandler1 = verifyStartSingleScanForImpl(mWifiScannerImpl1, order, nativeSettings); + } verifySuccessfulResponse(order, handler, requestId); verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource)); - when(mWifiScannerImpl.getLatestSingleScanResults()) - .thenReturn(results.getRawScanData()); - eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + when(mWifiScannerImpl0.getLatestSingleScanResults()) + .thenReturn(resultsForImpl0.getScanData()); + eventHandler0.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + if (resultsForImpl1 != null) { + when(mWifiScannerImpl1.getLatestSingleScanResults()) + .thenReturn(resultsForImpl1.getScanData()); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + } mLooper.dispatchAll(); - verifyScanResultsReceived(order, handler, requestId, results.getScanData()); + ScanResults expectedResults = resultsForImpl0; + if (resultsForImpl1 != null) { + expectedResults = ScanResults.merge( + resultsForImpl0.getScanData().getBandScanned(), + resultsForImpl0, resultsForImpl1); + } + verifyScanResultsReceived(order, handler, requestId, expectedResults.getScanData()); verifySingleScanCompletedReceived(order, handler, requestId); verifyNoMoreInteractions(handler); verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource)); assertDumpContainsRequestLog("addSingleScanRequest", requestId); assertDumpContainsCallbackLog("singleScanResults", requestId, - "results=" + results.getScanData().getResults().length); + "results=" + expectedResults.getScanData().getResults().length); } /** @@ -700,8 +759,8 @@ public class WifiScanningServiceTest { requestSettings.type = WifiScanner.TYPE_HIGH_ACCURACY; WorkSource workSource = new WorkSource(Binder.getCallingUid()); // don't explicitly set - when(mContext.checkPermission( - Manifest.permission.NETWORK_STACK, -1, Binder.getCallingUid())) + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) .thenReturn(PERMISSION_DENIED); startServiceAndLoadDriver(); @@ -709,10 +768,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); // successful start - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -732,7 +791,7 @@ public class WifiScanningServiceTest { // Ensure that no scan was triggered to the lower layers. verify(mBatteryStats, never()).noteWifiScanStoppedFromSource(eq(workSource)); - verify(mWifiScannerImpl, never()).startSingleScan(any(WifiNative.ScanSettings.class), + verify(mWifiScannerImpl0, never()).startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class)); } @@ -752,8 +811,8 @@ public class WifiScanningServiceTest { }; WorkSource workSource = new WorkSource(Binder.getCallingUid()); // don't explicitly set - when(mContext.checkPermission( - Manifest.permission.NETWORK_STACK, -1, Binder.getCallingUid())) + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) .thenReturn(PERMISSION_DENIED); startServiceAndLoadDriver(); @@ -761,10 +820,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); // successful start - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -784,7 +843,7 @@ public class WifiScanningServiceTest { // Ensure that no scan was triggered to the lower layers. verify(mBatteryStats, never()).noteWifiScanStoppedFromSource(eq(workSource)); - verify(mWifiScannerImpl, never()).startSingleScan(any(WifiNative.ScanSettings.class), + verify(mWifiScannerImpl0, never()).startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class)); } @@ -806,10 +865,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); // successful start - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -829,7 +888,7 @@ public class WifiScanningServiceTest { // Ensure that no scan was triggered to the lower layers. verify(mBatteryStats, never()).noteWifiScanStoppedFromSource(eq(workSource)); - verify(mWifiScannerImpl, never()).startSingleScan(any(WifiNative.ScanSettings.class), + verify(mWifiScannerImpl0, never()).startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class)); } @@ -839,8 +898,8 @@ public class WifiScanningServiceTest { @Test public void sendSingleScanRequestWithNoPrivilegedParamsSetFromNonPrivilegedApp() throws Exception { - when(mContext.checkPermission( - Manifest.permission.NETWORK_STACK, -1, Binder.getCallingUid())) + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) .thenReturn(PERMISSION_DENIED); WifiScanner.ScanSettings requestSettings = createRequest(channelsToSpec(2400, 5150, 5175), 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); @@ -863,10 +922,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); // scan fails - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(false); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -901,10 +960,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); // successful start - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -945,10 +1004,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); // successful start - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -978,10 +1037,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); // successful start - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -1005,12 +1064,12 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.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, mWifiScannerImpl0); // Run scan 1 sendSingleScanRequest(controlChannel, requestId, requestSettings, null); @@ -1028,6 +1087,46 @@ public class WifiScanningServiceTest { } /** + * Send a single scan request and then disable Wi-Fi before it completes + */ + @Test + public void sendSingleScanRequestThenDisableWifiAfterScanCompleteButBeforeReportingResults() { + WifiScanner.ScanSettings requestSettings = createRequest(channelsToSpec(2400), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + ScanResults results = ScanResults.create(0, WifiScanner.WIFI_BAND_UNSPECIFIED, 2400); + int requestId = 2293; + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0); + + // Run scan 1 + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order, + computeSingleScanNativeSettings(requestSettings)); + verifySuccessfulResponse(order, handler, requestId); + + // disable wifi + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_DISABLE)); + // scan results complete event + when(mWifiScannerImpl0.getLatestSingleScanResults()) + .thenReturn(results.getScanData()); + eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, requestId, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted"); + verifyNoMoreInteractions(handler); + } + + /** * Send a single scan request, schedule a second pending scan and disable Wi-Fi before the first * scan completes. */ @@ -1046,12 +1145,12 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.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, mWifiScannerImpl0); // Request scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); @@ -1103,12 +1202,12 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.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, mContext); + InOrder order = inOrder(handler, mWifiScannerImpl0, mContext); // Run scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); @@ -1119,7 +1218,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(order, handler, requestId1); // dispatch scan 1 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results1.getScanData()); eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1142,7 +1241,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(order, handler, requestId2); // dispatch scan 2 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results2.getScanData()); eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1171,13 +1270,13 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); InOrder handlerOrder = inOrder(handler); - InOrder nativeOrder = inOrder(mWifiScannerImpl); + InOrder nativeOrder = inOrder(mWifiScannerImpl0); // Run scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); @@ -1193,7 +1292,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(handlerOrder, handler, requestId2); // dispatch scan 1 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results1.getScanData()); eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1206,7 +1305,7 @@ public class WifiScanningServiceTest { computeSingleScanNativeSettings(requestSettings2)); // dispatch scan 2 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results2.getScanData()); eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1241,13 +1340,13 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); InOrder handlerOrder = inOrder(handler); - InOrder nativeOrder = inOrder(mWifiScannerImpl); + InOrder nativeOrder = inOrder(mWifiScannerImpl0); // Run scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); @@ -1263,7 +1362,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(handlerOrder, handler, requestId2); // dispatch scan 1 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results1.getScanData()); eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1276,7 +1375,7 @@ public class WifiScanningServiceTest { computeSingleScanNativeSettings(requestSettings2)); // dispatch scan 2 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results2.getScanData()); eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1334,13 +1433,13 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); InOrder handlerOrder = inOrder(handler); - InOrder nativeOrder = inOrder(mWifiScannerImpl); + InOrder nativeOrder = inOrder(mWifiScannerImpl0); // Run scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, workSource1); @@ -1364,7 +1463,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(handlerOrder, handler, requestId3); // dispatch scan 1 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results1.getScanData()); eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1379,7 +1478,7 @@ public class WifiScanningServiceTest { nativeSettings2and3); // dispatch scan 2 and 3 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results2and3.getScanData()); eventHandler2and3.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1435,13 +1534,13 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); InOrder handlerOrder = inOrder(handler); - InOrder nativeOrder = inOrder(mWifiScannerImpl); + InOrder nativeOrder = inOrder(mWifiScannerImpl0); // Run scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); @@ -1457,7 +1556,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(handlerOrder, handler, requestId2); // dispatch scan 1 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(resultsBoth.getScanData()); eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1510,13 +1609,13 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); InOrder handlerOrder = inOrder(handler); - InOrder nativeOrder = inOrder(mWifiScannerImpl); + InOrder nativeOrder = inOrder(mWifiScannerImpl0); // Run scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, workSource1); @@ -1540,7 +1639,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(handlerOrder, handler, requestId3); // dispatch scan 1 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results1and3.getScanData()); eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1556,7 +1655,7 @@ public class WifiScanningServiceTest { computeSingleScanNativeSettings(requestSettings2)); // dispatch scan 2 and 3 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results2.getScanData()); eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1595,7 +1694,7 @@ public class WifiScanningServiceTest { expectedResults); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage( Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); @@ -1625,7 +1724,7 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage( Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); @@ -1647,7 +1746,7 @@ public class WifiScanningServiceTest { mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage( Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); @@ -1674,7 +1773,7 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage( Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); @@ -1696,7 +1795,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(order, handler, secondScanRequestId); // dispatch scan 2 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(expectedSingleResult.getScanData()); eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1731,7 +1830,7 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage( Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); @@ -1756,7 +1855,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(order, handler, secondScanRequestId); // dispatch scan 2 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(expectedPartialResults.getScanData()); eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1805,7 +1904,7 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage( Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); @@ -1833,7 +1932,7 @@ public class WifiScanningServiceTest { expectedResults); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); controlChannel.sendMessage( Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); @@ -1874,9 +1973,9 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); registerScanListener(controlChannel, listenerRequestId); @@ -1889,7 +1988,7 @@ public class WifiScanningServiceTest { WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order, nativeSettings); verifySuccessfulResponse(order, handler, requestId); - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results.getRawScanData()); eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1922,9 +2021,9 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); registerScanListener(controlChannel, listenerRequestId); @@ -1940,7 +2039,7 @@ public class WifiScanningServiceTest { deregisterScanListener(controlChannel, listenerRequestId); mLooper.dispatchAll(); - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results.getRawScanData()); eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -1987,13 +2086,13 @@ public class WifiScanningServiceTest { startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); InOrder handlerOrder = inOrder(handler); - InOrder nativeOrder = inOrder(mWifiScannerImpl); + InOrder nativeOrder = inOrder(mWifiScannerImpl0); // Run scan 1 sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); @@ -2020,7 +2119,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(handlerOrder, handler, listenerRequestId); // dispatch scan 1 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results1.getScanData()); eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -2034,7 +2133,7 @@ public class WifiScanningServiceTest { nativeSettings2and3); // dispatch scan 2 and 3 results - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results2and3.getScanData()); eventHandler2and3.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -2051,9 +2150,25 @@ public class WifiScanningServiceTest { } @Test + public void rejectSingleScanRequestWhenScannerGetIfaceNameFails() throws Exception { + // Failed to get client interface name. + when(mWifiNative.getClientInterfaceNames()).thenReturn(new ArraySet<>()); + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler); + sendSingleScanRequest(controlChannel, 122, generateValidScanSettings(), null); + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, 122, WifiScanner.REASON_UNSPECIFIED, "not available"); + } + + @Test public void rejectSingleScanRequestWhenScannerImplCreateFails() throws Exception { // Fail scanner impl creation. - when(mWifiScannerImplFactory.create(any(), any(), any())).thenReturn(null); + when(mWifiScannerImplFactory.create(any(), any(), any(), any())).thenReturn(null); startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); @@ -2066,7 +2181,6 @@ public class WifiScanningServiceTest { verifyFailedResponse(order, handler, 122, WifiScanner.REASON_UNSPECIFIED, "not available"); } - private void doSuccessfulBackgroundScan(WifiScanner.ScanSettings requestSettings, WifiNative.ScanSettings nativeSettings) { startServiceAndLoadDriver(); @@ -2074,9 +2188,9 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); - when(mWifiScannerImpl.startBatchedScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startBatchedScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendBackgroundScanRequest(controlChannel, 12, requestSettings, null); @@ -2173,13 +2287,13 @@ public class WifiScanningServiceTest { return ScanResults.create(0, 2400, 5150, 5175); } - private WifiNative.PnoEventHandler verifyHwPno(InOrder order, + private WifiNative.PnoEventHandler verifyHwPnoForImpl(WifiScannerImpl impl, InOrder order, WifiNative.PnoSettings expected) { ArgumentCaptor<WifiNative.PnoSettings> pnoSettingsCaptor = ArgumentCaptor.forClass(WifiNative.PnoSettings.class); ArgumentCaptor<WifiNative.PnoEventHandler> pnoEventHandlerCaptor = ArgumentCaptor.forClass(WifiNative.PnoEventHandler.class); - order.verify(mWifiScannerImpl).setHwPnoList(pnoSettingsCaptor.capture(), + order.verify(impl).setHwPnoList(pnoSettingsCaptor.capture(), pnoEventHandlerCaptor.capture()); assertNativePnoSettingsEquals(expected, pnoSettingsCaptor.getValue()); return pnoEventHandlerCaptor.getValue(); @@ -2213,7 +2327,7 @@ public class WifiScanningServiceTest { private void expectSuccessfulBackgroundScan(InOrder order, WifiNative.ScanSettings nativeSettings, ScanResults results) { - when(mWifiScannerImpl.startBatchedScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startBatchedScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); mLooper.dispatchAll(); WifiNative.ScanEventHandler eventHandler = verifyStartBackgroundScan(order, nativeSettings); @@ -2222,27 +2336,52 @@ public class WifiScanningServiceTest { for (ScanResult fullScanResult : results.getRawScanResults()) { eventHandler.onFullScanResult(fullScanResult, 0); } - when(mWifiScannerImpl.getLatestBatchedScanResults(anyBoolean())).thenReturn(scanDatas); + when(mWifiScannerImpl0.getLatestBatchedScanResults(anyBoolean())).thenReturn(scanDatas); eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); mLooper.dispatchAll(); } private void expectHwPnoScan(InOrder order, Handler handler, int requestId, WifiNative.PnoSettings nativeSettings, ScanResults results) { - when(mWifiScannerImpl.isHwPnoSupported(anyBoolean())).thenReturn(true); + when(mWifiScannerImpl0.isHwPnoSupported(anyBoolean())).thenReturn(true); - when(mWifiScannerImpl.setHwPnoList(any(WifiNative.PnoSettings.class), + when(mWifiScannerImpl0.setHwPnoList(any(WifiNative.PnoSettings.class), any(WifiNative.PnoEventHandler.class))).thenReturn(true); mLooper.dispatchAll(); - WifiNative.PnoEventHandler eventHandler = verifyHwPno(order, nativeSettings); + WifiNative.PnoEventHandler eventHandler = + verifyHwPnoForImpl(mWifiScannerImpl0, order, nativeSettings); verifySuccessfulResponse(order, handler, requestId); eventHandler.onPnoNetworkFound(results.getRawScanResults()); mLooper.dispatchAll(); } + private void expectHwPnoScanOnImpls(InOrder order, Handler handler, + int requestId, WifiNative.PnoSettings nativeSettings, + @Nullable ScanResults resultsForImpl0, @Nullable ScanResults resultsForImpl1) { + when(mWifiScannerImpl0.isHwPnoSupported(anyBoolean())).thenReturn(true); + when(mWifiScannerImpl1.isHwPnoSupported(anyBoolean())).thenReturn(true); + + when(mWifiScannerImpl0.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(true); + when(mWifiScannerImpl1.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(true); + mLooper.dispatchAll(); + WifiNative.PnoEventHandler eventHandler0 = + verifyHwPnoForImpl(mWifiScannerImpl0, order, nativeSettings); + WifiNative.PnoEventHandler eventHandler1 = + verifyHwPnoForImpl(mWifiScannerImpl1, order, nativeSettings); + verifySuccessfulResponse(order, handler, requestId); + if (resultsForImpl0 != null) { + eventHandler0.onPnoNetworkFound(resultsForImpl0.getRawScanResults()); + } else if (resultsForImpl1 != null) { + eventHandler1.onPnoNetworkFound(resultsForImpl1.getRawScanResults()); + } + mLooper.dispatchAll(); + } + /** - * 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. + * Tests wificond PNO scan. 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 { @@ -2250,7 +2389,7 @@ public class WifiScanningServiceTest { mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); int requestId = 12; ScanResults scanResults = createScanResultsForPno(); @@ -2267,14 +2406,14 @@ public class WifiScanningServiceTest { @Test public void rejectHwPnoScanRequestWhenScannerImplCreateFails() throws Exception { // Fail scanner impl creation. - when(mWifiScannerImplFactory.create(any(), any(), any())).thenReturn(null); + when(mWifiScannerImplFactory.create(any(), any(), any(), any())).thenReturn(null); startServiceAndLoadDriver(); mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); ScanResults scanResults = createScanResultsForPno(); Pair<WifiScanner.ScanSettings, WifiNative.ScanSettings> scanSettings = @@ -2338,13 +2477,13 @@ public class WifiScanningServiceTest { BidirectionalAsyncChannel controlChannel = connectChannel(handler); mLooper.dispatchAll(); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); ScanResults results = ScanResults.create(0, WifiScanner.WIFI_BAND_BOTH, 2400); - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results.getRawScanData()); - InOrder order = inOrder(mWifiScannerImpl, handler); + InOrder order = inOrder(mWifiScannerImpl0, handler); sendSingleScanRequest(controlChannel, requestId, requestSettings, null); mLooper.dispatchAll(); @@ -2390,10 +2529,10 @@ public class WifiScanningServiceTest { // Ensure we didn't create scanner instance twice. verify(mWifiScannerImplFactory, times(1)) - .create(any(), any(), any()); + .create(any(), any(), any(), any()); InOrder order = inOrder(handler); - when(mWifiScannerImpl.startBatchedScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startBatchedScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendBackgroundScanRequest(controlChannel, 192, generateValidScanSettings(), null); mLooper.dispatchAll(); @@ -2416,9 +2555,9 @@ public class WifiScanningServiceTest { // Ensure we didn't create scanner instance twice. verify(mWifiScannerImplFactory, times(1)) - .create(any(), any(), any()); + .create(any(), any(), any(), any()); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); int requestId = 12; WorkSource workSource = new WorkSource(2292); @@ -2427,7 +2566,7 @@ public class WifiScanningServiceTest { ScanResults results = ScanResults.create(0, WifiScanner.WIFI_BAND_UNSPECIFIED, 2400, 5150, 5175); - when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); sendSingleScanRequest(controlChannel, requestId, requestSettings, workSource); @@ -2438,7 +2577,7 @@ public class WifiScanningServiceTest { verifySuccessfulResponse(order, handler, requestId); verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource)); - when(mWifiScannerImpl.getLatestSingleScanResults()) + when(mWifiScannerImpl0.getLatestSingleScanResults()) .thenReturn(results.getRawScanData()); eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -2467,9 +2606,9 @@ public class WifiScanningServiceTest { // Ensure we didn't create scanner instance twice. verify(mWifiScannerImplFactory, times(1)) - .create(any(), any(), any()); + .create(any(), any(), any(), any()); - InOrder order = inOrder(handler, mWifiScannerImpl); + InOrder order = inOrder(handler, mWifiScannerImpl0); int requestId = 12; ScanResults scanResults = createScanResultsForPno(); @@ -2484,7 +2623,7 @@ public class WifiScanningServiceTest { } /** - * Verifies that only clients with NETWORK_STACK permission can issues restricted messages + * Verifies that only clients with PERMISSION_MAINLINE_WIFI_STACK permission can issues restricted messages * (from API's). */ @Test @@ -2494,9 +2633,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - // Client doesn't have NETWORK_STACK permission. + // Client doesn't have PERMISSION_MAINLINE_WIFI_STACK permission. doThrow(new SecurityException()).when(mContext).enforcePermission( - eq(Manifest.permission.NETWORK_STACK), anyInt(), eq(Binder.getCallingUid()), any()); + eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), anyInt(), + eq(Binder.getCallingUid()), any()); controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_ENABLE)); mLooper.dispatchAll(); @@ -2529,18 +2669,25 @@ public class WifiScanningServiceTest { "Not authorized", messageCaptor.getAllValues().get(4)); // Ensure we didn't create scanner instance. - verify(mWifiScannerImplFactory, never()).create(any(), any(), any()); + verify(mWifiScannerImplFactory, never()).create(any(), any(), any(), any()); } /** - * Verifies that clients without NETWORK_STACK permission cannot issue any messages when they + * Verifies that clients without PERMISSION_MAINLINE_WIFI_STACK permission cannot issue any messages when they * don't have the necessary location permissions & location is enabled. */ @Test public void rejectAllMessagesFromNonPrivilegedAppsWithoutLocationPermission() throws Exception { // Start service & initialize it. startServiceAndLoadDriver(); + doThrow(new SecurityException()).when(mContext).enforcePermission( + eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), anyInt(), + eq(Binder.getCallingUid()), any()); + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) + .thenReturn(PERMISSION_DENIED); + // Location permission or mode check fail. doThrow(new SecurityException()).when(mWifiPermissionsUtil) .enforceCanAccessScanResultsForWifiScanner(any(), eq(Binder.getCallingUid()), @@ -2549,9 +2696,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - // Client doesn't have NETWORK_STACK permission. - doThrow(new SecurityException()).when(mContext).enforcePermission( - eq(Manifest.permission.NETWORK_STACK), anyInt(), eq(Binder.getCallingUid()), any()); + // Client doesn't have PERMISSION_MAINLINE_WIFI_STACK permission. + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) + .thenReturn(PERMISSION_DENIED); controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_START_SINGLE_SCAN)); mLooper.dispatchAll(); @@ -2574,11 +2722,11 @@ public class WifiScanningServiceTest { "Not authorized", messageCaptor.getAllValues().get(2)); // Validate the initialization sequence. - verify(mWifiScannerImpl).getChannelHelper(); - verify(mWifiScannerImpl).getScanCapabilities(any()); + verify(mWifiScannerImpl0).getChannelHelper(); + verify(mWifiScannerImpl0).getScanCapabilities(any()); // Ensure we didn't start any scans after. - verifyNoMoreInteractions(mWifiScannerImpl); + verifyNoMoreInteractions(mWifiScannerImpl0); } /** @@ -2593,9 +2741,13 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - // Client doesn't have NETWORK_STACK permission. + // Client doesn't have PERMISSION_MAINLINE_WIFI_STACK permission. doThrow(new SecurityException()).when(mContext).enforcePermission( - eq(Manifest.permission.NETWORK_STACK), anyInt(), eq(Binder.getCallingUid()), any()); + eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), anyInt(), + eq(Binder.getCallingUid()), any()); + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) + .thenReturn(PERMISSION_DENIED); Bundle bundle = new Bundle(); bundle.putString(WifiScanner.REQUEST_PACKAGE_NAME_KEY, TEST_PACKAGE_NAME); @@ -2653,9 +2805,13 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - // Client doesn't have NETWORK_STACK permission. + // Client doesn't have PERMISSION_MAINLINE_WIFI_STACK permission. doThrow(new SecurityException()).when(mContext).enforcePermission( - eq(Manifest.permission.NETWORK_STACK), anyInt(), eq(Binder.getCallingUid()), any()); + eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), anyInt(), + eq(Binder.getCallingUid()), any()); + when(mContext.checkPermission(eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), + anyInt(), eq(Binder.getCallingUid()))) + .thenReturn(PERMISSION_DENIED); Bundle bundle = new Bundle(); bundle.putString(WifiScanner.REQUEST_PACKAGE_NAME_KEY, TEST_PACKAGE_NAME); @@ -2715,9 +2871,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - // Client does have NETWORK_STACK permission. + // Client does have WIFI_STACK permission. doNothing().when(mContext).enforcePermission( - eq(Manifest.permission.NETWORK_STACK), anyInt(), eq(Binder.getCallingUid()), any()); + eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), anyInt(), + eq(Binder.getCallingUid()), any()); Bundle bundle = new Bundle(); bundle.putString(WifiScanner.REQUEST_PACKAGE_NAME_KEY, TEST_PACKAGE_NAME); @@ -2737,4 +2894,648 @@ public class WifiScanningServiceTest { verify(mWifiPermissionsUtil, never()).enforceCanAccessScanResultsForWifiScanner( eq(TEST_PACKAGE_NAME), eq(Binder.getCallingUid()), anyBoolean(), anyBoolean()); } + + /** + * Setup/teardown a second scanner impl dynamically. + */ + @Test + public void setupAndTeardownSecondImpl() throws Exception { + // start up service with a single impl. + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + verify(mWifiScannerImplFactory, times(1)) + .create(any(), any(), any(), eq(TEST_IFACE_NAME_0)); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler); + + // Now setup an impl for second iface. + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_ENABLE)); + mLooper.dispatchAll(); + + verify(mWifiScannerImplFactory, times(1)) + .create(any(), any(), any(), eq(TEST_IFACE_NAME_1)); + + // Now teardown the impl for second iface. + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0))); + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_ENABLE)); + mLooper.dispatchAll(); + + verify(mWifiScannerImpl1).cleanup(); + + // Now teardown everything. + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_DISABLE)); + mLooper.dispatchAll(); + + verify(mWifiScannerImpl0).cleanup(); + } + + /** + * Setup/teardown a second scanner impl dynamically which satisfies the same set of channels + * as the existing one. + */ + @Test + public void setupAndTeardownSecondImplWhichSatisfiesExistingImpl() throws Exception { + // start up service with a single impl. + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + verify(mWifiScannerImplFactory, times(1)) + .create(any(), any(), any(), eq(TEST_IFACE_NAME_0)); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler); + + // Now setup an impl for second iface. + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + // Setup the second impl to contain the same set of channels as the first one. + when(mWifiScannerImpl1.getChannelHelper()).thenReturn(mChannelHelper0); + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_ENABLE)); + mLooper.dispatchAll(); + + // Verify that we created the new impl and immediately tore it down because it was + // satisfied by an existing impl. + verify(mWifiScannerImplFactory, times(1)) + .create(any(), any(), any(), eq(TEST_IFACE_NAME_1)); + verify(mWifiScannerImpl1, times(1)).getChannelHelper(); + verify(mWifiScannerImpl1, times(1)).cleanup(); + + // Now teardown the second iface. + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0))); + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_ENABLE)); + mLooper.dispatchAll(); + + // do nothing, since impl1 was never added to the active impl list. + verifyNoMoreInteractions(mWifiScannerImpl1); + + // Now teardown everything. + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_DISABLE)); + mLooper.dispatchAll(); + + verify(mWifiScannerImpl0).cleanup(); + } + + /** + * Setup a second scanner impl and tearddown a existing scanning impl dynamically which + * satisfies the same set of channels as the existing one. + */ + @Test + public void setupSecondImplAndTeardownFirstImplWhichSatisfiesExistingImpl() throws Exception { + // start up service with a single impl. + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + verify(mWifiScannerImplFactory, times(1)) + .create(any(), any(), any(), eq(TEST_IFACE_NAME_0)); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler); + + + // Now setup an impl for second iface and teardown the first one. + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_1))); + // Setup the second impl to contain the same set of channels as the first one. + when(mWifiScannerImpl1.getChannelHelper()).thenReturn(mChannelHelper0); + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_ENABLE)); + mLooper.dispatchAll(); + + // tear down the first one because corresponding iface was brought down. + verify(mWifiScannerImpl0).cleanup(); + + // Verify that we created the new impl. + verify(mWifiScannerImplFactory, times(1)) + .create(any(), any(), any(), eq(TEST_IFACE_NAME_1)); + verify(mWifiScannerImpl1, never()).getChannelHelper(); + + // Now teardown everything. + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_DISABLE)); + mLooper.dispatchAll(); + + verify(mWifiScannerImpl1).cleanup(); + } + + /** + * Do a single scan for a band and verify that it is successful across multiple impls. + */ + @Test + public void sendSingleScanBandRequestOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + WifiScanner.ScanSettings requestSettings = createRequest( + WifiScanner.WIFI_BAND_BOTH_WITH_DFS, 0, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + doSuccessfulSingleScanOnImpls(requestSettings, + computeSingleScanNativeSettings(requestSettings), + ScanResults.create(0, WifiScanner.WIFI_BAND_BOTH_WITH_DFS, 2400), + ScanResults.create(0, WifiScanner.WIFI_BAND_BOTH_WITH_DFS, 5150)); + } + + /** + * Do a single scan for a list of channels and verify that it is successful across multiple + * impls. + */ + @Test + public void sendSingleScanChannelsRequestOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + WifiScanner.ScanSettings requestSettings = createRequest(channelsToSpec(2400, 5150, 5175), + 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + doSuccessfulSingleScanOnImpls(requestSettings, + computeSingleScanNativeSettings(requestSettings), + ScanResults.create(0, WifiScanner.WIFI_BAND_UNSPECIFIED, 2400), + ScanResults.create(0, WifiScanner.WIFI_BAND_UNSPECIFIED, 5175)); + } + + /** + * Do a single scan with no results and verify that it is successful across multiple + * impls. + */ + @Test + public void sendSingleScanRequestWithNoResultsOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + doSuccessfulSingleScanOnImpls(requestSettings, + computeSingleScanNativeSettings(requestSettings), + ScanResults.create(0, WifiScanner.WIFI_BAND_BOTH, new int[0]), + ScanResults.create(0, WifiScanner.WIFI_BAND_BOTH, new int[0])); + } + + /** + * Do a single scan, which the hardware fails to start across multiple impls, and verify that a + * failure response is delivered. + */ + @Test + public void sendSingleScanRequestWhichFailsToStartOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId = 33; + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + + // scan fails + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(false); + when(mWifiScannerImpl1.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(false); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + + mLooper.dispatchAll(); + // Scan is successfully queue, but then fails to execute + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + order.verify(handler, times(2)).handleMessage(messageCaptor.capture()); + assertSuccessfulResponse(requestId, messageCaptor.getAllValues().get(0)); + assertFailedResponse(requestId, WifiScanner.REASON_UNSPECIFIED, + "Failed to start single scan", messageCaptor.getAllValues().get(1)); + verifyNoMoreInteractions(mBatteryStats); + + assertEquals(mWifiMetrics.getOneshotScanCount(), 1); + assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_UNKNOWN), 1); + assertDumpContainsRequestLog("addSingleScanRequest", requestId); + } + + /** + * Do a single scan, which the hardware fails to start on one of the impl, and verify that a + * successful response is delivered when other impls succeed. + */ + @Test + public void sendSingleScanRequestWhichFailsToStartOnOneImpl() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiNative.ScanSettings nativeSettings = computeSingleScanNativeSettings(requestSettings); + ScanResults results = + ScanResults.create(0, WifiScanner.WIFI_BAND_UNSPECIFIED, 2400, 5150, 5175); + int requestId = 33; + WorkSource workSource = new WorkSource(2292); + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + + // scan fails on impl0 + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(false); + // scan succeeds on impl1 + when(mWifiScannerImpl1.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, workSource); + + mLooper.dispatchAll(); + + WifiNative.ScanEventHandler eventHandler0 = + verifyStartSingleScanForImpl(mWifiScannerImpl0, order, nativeSettings); + WifiNative.ScanEventHandler eventHandler1 = + verifyStartSingleScanForImpl(mWifiScannerImpl1, order, nativeSettings); + verifySuccessfulResponse(order, handler, requestId); + verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource)); + + when(mWifiScannerImpl0.getLatestSingleScanResults()) + .thenReturn(new WifiScanner.ScanData(DUMMY_SCAN_DATA)); + // Send scan success on impl1 + when(mWifiScannerImpl1.getLatestSingleScanResults()) + .thenReturn(results.getRawScanData()); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsReceived(order, handler, requestId, results.getScanData()); + verifySingleScanCompletedReceived(order, handler, requestId); + verifyNoMoreInteractions(handler); + verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource)); + assertDumpContainsRequestLog("addSingleScanRequest", requestId); + assertDumpContainsCallbackLog("singleScanResults", requestId, + "results=" + results.getScanData().getResults().length); + } + + /** + * Do a single scan, which successfully starts, but fails across multiple impls partway through + * and verify that a failure response is delivered. + */ + @Test + public void sendSingleScanRequestWhichFailsAfterStartOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId = 33; + WorkSource workSource = new WorkSource(Binder.getCallingUid()); // don't explicitly set + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + + // successful start + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + when(mWifiScannerImpl1.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + + // Scan is successfully queue + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler0 = + verifyStartSingleScanForImpl(mWifiScannerImpl0, order, + computeSingleScanNativeSettings(requestSettings)); + WifiNative.ScanEventHandler eventHandler1 = + verifyStartSingleScanForImpl(mWifiScannerImpl1, order, + computeSingleScanNativeSettings(requestSettings)); + verifySuccessfulResponse(order, handler, requestId); + verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource)); + + // but then fails to execute + eventHandler0.onScanStatus(WifiNative.WIFI_SCAN_FAILED); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_FAILED); + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, requestId, + WifiScanner.REASON_UNSPECIFIED, "Scan failed"); + assertDumpContainsCallbackLog("singleScanFailed", requestId, + "reason=" + WifiScanner.REASON_UNSPECIFIED + ", Scan failed"); + assertEquals(mWifiMetrics.getOneshotScanCount(), 1); + assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_UNKNOWN), 1); + verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource)); + } + + /** + * Do a single scan, which successfully starts, but fails partway through on one of the impls + * and verify that a successful response is delivered. + */ + @Test + public void sendSingleScanRequestWhichFailsAfterStartOnOneImpl() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiNative.ScanSettings nativeSettings = computeSingleScanNativeSettings(requestSettings); + ScanResults results = + ScanResults.create(0, WifiScanner.WIFI_BAND_UNSPECIFIED, 2400, 5150, 5175); + int requestId = 33; + WorkSource workSource = new WorkSource(Binder.getCallingUid()); // don't explicitly set + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + + // successful start + when(mWifiScannerImpl0.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + when(mWifiScannerImpl1.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + + // Scan is successfully queued + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler0 = + verifyStartSingleScanForImpl(mWifiScannerImpl0, order, nativeSettings); + WifiNative.ScanEventHandler eventHandler1 = + verifyStartSingleScanForImpl(mWifiScannerImpl1, order, nativeSettings); + verifySuccessfulResponse(order, handler, requestId); + verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource)); + + // then fails to execute on impl0 + when(mWifiScannerImpl0.getLatestSingleScanResults()) + .thenReturn(new WifiScanner.ScanData(DUMMY_SCAN_DATA)); + eventHandler0.onScanStatus(WifiNative.WIFI_SCAN_FAILED); + // but succeeds on impl1 + when(mWifiScannerImpl1.getLatestSingleScanResults()) + .thenReturn(results.getRawScanData()); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsReceived(order, handler, requestId, results.getScanData()); + verifySingleScanCompletedReceived(order, handler, requestId); + verifyNoMoreInteractions(handler); + verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource)); + assertDumpContainsRequestLog("addSingleScanRequest", requestId); + assertDumpContainsCallbackLog("singleScanResults", requestId, + "results=" + results.getScanData().getResults().length); + } + + /** + * Tests wificond PNO scan across multiple impls. This ensures that the + * PNO scan results are plumbed back to the client as a PNO network found event. + */ + @Test + public void testSuccessfulHwPnoScanOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + int requestId = 12; + + ScanResults scanResults = createScanResultsForPno(); + Pair<WifiScanner.ScanSettings, WifiNative.ScanSettings> scanSettings = + createScanSettingsForHwPno(); + Pair<WifiScanner.PnoSettings, WifiNative.PnoSettings> pnoSettings = + createPnoSettings(scanResults); + + // Results received on impl 0 + sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first); + expectHwPnoScanOnImpls(order, handler, requestId, pnoSettings.second, scanResults, null); + verifyPnoNetworkFoundReceived(order, handler, requestId, scanResults.getRawScanResults()); + } + + /** + * Tests wificond PNO scan that fails to start on all impls. + */ + @Test + public void testFailedHwPnoScanWhichFailsToStartOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + int requestId = 12; + + ScanResults scanResults = createScanResultsForPno(); + Pair<WifiScanner.ScanSettings, WifiNative.ScanSettings> scanSettings = + createScanSettingsForHwPno(); + Pair<WifiScanner.PnoSettings, WifiNative.PnoSettings> pnoSettings = + createPnoSettings(scanResults); + + when(mWifiScannerImpl0.isHwPnoSupported(anyBoolean())).thenReturn(true); + when(mWifiScannerImpl1.isHwPnoSupported(anyBoolean())).thenReturn(true); + // pno scan fails on both impls + when(mWifiScannerImpl0.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(false); + when(mWifiScannerImpl1.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(false); + + sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first); + mLooper.dispatchAll(); + + verifyFailedResponse(order, handler, requestId, WifiScanner.REASON_INVALID_REQUEST, + "bad request"); + } + + /** + * Tests wificond PNO scan that fails to start on one of the impls. + */ + @Test + public void testSuccessfulHwPnoScanWhichFailsToStartOnOneImpl() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + int requestId = 12; + + ScanResults scanResults = createScanResultsForPno(); + Pair<WifiScanner.ScanSettings, WifiNative.ScanSettings> scanSettings = + createScanSettingsForHwPno(); + Pair<WifiScanner.PnoSettings, WifiNative.PnoSettings> pnoSettings = + createPnoSettings(scanResults); + + when(mWifiScannerImpl0.isHwPnoSupported(anyBoolean())).thenReturn(true); + when(mWifiScannerImpl1.isHwPnoSupported(anyBoolean())).thenReturn(true); + // pno scan fails on impl0 + when(mWifiScannerImpl0.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(false); + // pno scan succeeds on impl1 + when(mWifiScannerImpl1.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(true); + + sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first); + mLooper.dispatchAll(); + + WifiNative.PnoEventHandler eventHandler0 = + verifyHwPnoForImpl(mWifiScannerImpl0, order, pnoSettings.second); + WifiNative.PnoEventHandler eventHandler1 = + verifyHwPnoForImpl(mWifiScannerImpl1, order, pnoSettings.second); + + verifySuccessfulResponse(order, handler, requestId); + + eventHandler1.onPnoNetworkFound(scanResults.getRawScanResults()); + mLooper.dispatchAll(); + + verifyPnoNetworkFoundReceived(order, handler, requestId, scanResults.getRawScanResults()); + } + + /** + * Tests wificond PNO scan that fails after start on all impls. + */ + @Test + public void testFailedHwPnoScanWhichFailsAfterStartOnMultipleImpls() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + int requestId = 12; + + ScanResults scanResults = createScanResultsForPno(); + Pair<WifiScanner.ScanSettings, WifiNative.ScanSettings> scanSettings = + createScanSettingsForHwPno(); + Pair<WifiScanner.PnoSettings, WifiNative.PnoSettings> pnoSettings = + createPnoSettings(scanResults); + + when(mWifiScannerImpl0.isHwPnoSupported(anyBoolean())).thenReturn(true); + when(mWifiScannerImpl1.isHwPnoSupported(anyBoolean())).thenReturn(true); + // pno scan succeeds + when(mWifiScannerImpl0.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(true); + when(mWifiScannerImpl1.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(true); + + sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first); + mLooper.dispatchAll(); + + WifiNative.PnoEventHandler eventHandler0 = + verifyHwPnoForImpl(mWifiScannerImpl0, order, pnoSettings.second); + WifiNative.PnoEventHandler eventHandler1 = + verifyHwPnoForImpl(mWifiScannerImpl1, order, pnoSettings.second); + + verifySuccessfulResponse(order, handler, requestId); + + // fails afterwards. + eventHandler0.onPnoScanFailed(); + eventHandler1.onPnoScanFailed(); + mLooper.dispatchAll(); + + // Scan is successfully queue, but then fails to execute + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + order.verify(handler).handleMessage(messageCaptor.capture()); + assertFailedResponse(requestId, WifiScanner.REASON_UNSPECIFIED, + "pno scan failed", messageCaptor.getValue()); + } + + /** + * Tests wificond PNO scan that fails after start on one impls. + */ + @Test + public void testSuccessfulHwPnoScanWhichFailsAfterStartOnOneImpl() throws Exception { + when(mWifiNative.getClientInterfaceNames()) + .thenReturn(new ArraySet<>(Arrays.asList(TEST_IFACE_NAME_0, TEST_IFACE_NAME_1))); + + startServiceAndLoadDriver(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl0, mWifiScannerImpl1); + int requestId = 12; + + ScanResults scanResults = createScanResultsForPno(); + Pair<WifiScanner.ScanSettings, WifiNative.ScanSettings> scanSettings = + createScanSettingsForHwPno(); + Pair<WifiScanner.PnoSettings, WifiNative.PnoSettings> pnoSettings = + createPnoSettings(scanResults); + + when(mWifiScannerImpl0.isHwPnoSupported(anyBoolean())).thenReturn(true); + when(mWifiScannerImpl1.isHwPnoSupported(anyBoolean())).thenReturn(true); + // pno scan succeeds + when(mWifiScannerImpl0.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(true); + when(mWifiScannerImpl1.setHwPnoList(any(WifiNative.PnoSettings.class), + any(WifiNative.PnoEventHandler.class))).thenReturn(true); + + sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first); + mLooper.dispatchAll(); + + WifiNative.PnoEventHandler eventHandler0 = + verifyHwPnoForImpl(mWifiScannerImpl0, order, pnoSettings.second); + WifiNative.PnoEventHandler eventHandler1 = + verifyHwPnoForImpl(mWifiScannerImpl1, order, pnoSettings.second); + + verifySuccessfulResponse(order, handler, requestId); + + // fails afterwards on impl0. + eventHandler0.onPnoScanFailed(); + // pno match on impl1. + eventHandler1.onPnoNetworkFound(scanResults.getRawScanResults()); + mLooper.dispatchAll(); + + verifyPnoNetworkFoundReceived(order, handler, requestId, scanResults.getRawScanResults()); + } + + /** + * Tests that {@link WifiScanningServiceImpl#getAvailableChannels(int, String)} throws a + * {@link SecurityException} if the caller doesn't hold the required permissions. + */ + @Test(expected = SecurityException.class) + public void getAvailableChannels_noPermission_throwsException() throws Exception { + startServiceAndLoadDriver(); + + // no MAINLINE_WIFI_STACK permission + doThrow(new SecurityException()).when(mContext).enforcePermission( + eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), anyInt(), + eq(Binder.getCallingUid()), any()); + + // Location permission or mode check fail. + doThrow(new SecurityException()) + .when(mWifiPermissionsUtil).enforceCanAccessScanResultsForWifiScanner( + TEST_PACKAGE_NAME, Binder.getCallingUid(), false, false); + + mWifiScanningServiceImpl.getAvailableChannels(WifiScanner.WIFI_BAND_24_GHZ, + TEST_PACKAGE_NAME); + } + + /** + * Tests that {@link WifiScanningServiceImpl#getAvailableChannels(int, String)} returns + * the expected result if the caller does hold the required permissions. + */ + @Test + public void getAvailableChannels_hasPermission_returnsSuccessfully() throws Exception { + startServiceAndLoadDriver(); + + // has MAINLINE_WIFI_STACK permission + doNothing().when(mContext).enforcePermission( + eq(WifiStackClient.PERMISSION_MAINLINE_WIFI_STACK), anyInt(), + eq(Binder.getCallingUid()), any()); + + // has access scan results permission + doNothing().when(mWifiPermissionsUtil).enforceCanAccessScanResultsForWifiScanner( + TEST_PACKAGE_NAME, Binder.getCallingUid(), false, false); + + Bundle bundle = mWifiScanningServiceImpl.getAvailableChannels( + WifiScanner.WIFI_BAND_24_GHZ, TEST_PACKAGE_NAME); + List<Integer> actual = bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA); + + List<Integer> expected = Arrays.asList(2400, 2450); + assertEquals(expected, actual); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java index 8dcc177198..e4b6ec5784 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java @@ -35,6 +35,7 @@ import com.android.server.wifi.Clock; import com.android.server.wifi.MockResources; import com.android.server.wifi.MockWifiMonitor; import com.android.server.wifi.ScanResults; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiMonitor; import com.android.server.wifi.WifiNative; import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; @@ -52,7 +53,7 @@ import java.util.Set; * Unit tests for {@link com.android.server.wifi.scanner.WificondScannerImpl.setPnoList}. */ @SmallTest -public class WificondPnoScannerTest { +public class WificondPnoScannerTest extends WifiBaseTest { private static final String IFACE_NAME = "a_test_interface_name"; @Mock Context mContext; diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java index 4f2492042d..c2a09fe27e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java @@ -28,6 +28,7 @@ import android.net.wifi.WifiScanner; import androidx.test.filters.SmallTest; +import com.android.server.wifi.ScanDetail; import com.android.server.wifi.ScanResults; import com.android.server.wifi.WifiMonitor; import com.android.server.wifi.WifiNative; @@ -35,12 +36,16 @@ import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.regex.Pattern; /** @@ -48,6 +53,10 @@ import java.util.regex.Pattern; */ @SmallTest public class WificondScannerTest extends BaseWifiScannerImplTest { + private static final String NATIVE_SCAN_TITLE = "Latest native scan results:"; + private static final String NATIVE_PNO_SCAN_TITLE = "Latest native pno scan results:"; + private static final String NATIVE_SCAN_IE_TITLE = "Latest native scan results IEs:"; + WifiMonitor mWifiMonitorSpy; @Before public void setup() throws Exception { @@ -163,11 +172,97 @@ public class WificondScannerTest extends BaseWifiScannerImplTest { } /** - * Test that dump() of WificondScannerImpl dumps native scan results. + * Test that dump() of WificondScannerImpl dumps native scan results with correct format when + * scan result is empty. */ @Test - public void dumpContainsNativeScanResults() { - assertDumpContainsRequestLog("Latest native scan results:"); + public void testDumpWithCorrectFormatWithEmptyScanResult() { + // The format of scan dump when zero Ap in scan result. + // --------------------------------------------- + // "Latest native scan results:\n" : Key word can't modify. + // "Latest native pno scan results:\n": Key word can't modify. + // " ... \n": Any String and any line. No limited. + // "Latest native scan results IEs:\n": Key word can't modify. + // "\n" : Should be stop with "\n" + //--------------------------------------------- + Pattern zeroScanResultRegex = Pattern.compile( + "" + NATIVE_SCAN_TITLE + "\n" + + "" + NATIVE_PNO_SCAN_TITLE + "\n" + + "(.*\n)*" + + "" + NATIVE_SCAN_IE_TITLE + "\n" + + "\n"); + + String dump = dumpObject(); + + assertLogContainsRequestPattern(zeroScanResultRegex, dump); + + } + + /** + * Test that dump() of WificondScannerImpl dumps native scan results with correct format when + * the number of AP in the scan result is not zero. + */ + @Test + public void testDumpWithCorrectFormatWithScanResult() { + // Prepare the setting to trigger a scan. + WifiNative.ScanSettings settings = new NativeScanSettingsBuilder() + .withBasePeriod(10000) + .withMaxApPerScan(2) + .addBucketWithBand(10000, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN + | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, + WifiScanner.WIFI_BAND_24_GHZ) + .build(); + long approxScanStartUs = mClock.getElapsedSinceBootMillis() * 1000; + ArrayList<ScanDetail> rawScanResults = new ArrayList<>(Arrays.asList( + ScanResults.generateNativeResults(0, 5150, 5171))); + WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); + InOrder order = inOrder(eventHandler, mWifiNative); + // scan succeeds + when(mWifiNative.scan(eq(IFACE_NAME), anyInt(), any(), any(List.class))).thenReturn(true); + when(mWifiNative.getScanResults(eq(IFACE_NAME))).thenReturn(rawScanResults); + + int ap_count = rawScanResults.size(); + // The format of scan dump when the number of AP in the scan result is not zero. + // --------------------------------------------- + // "Latest native scan results:\n" : Key word can't modify. + // " BSSID ... \n" : Must start with 4 spaces but only show when + // the number of AP in the scan result is not zero. + // " The APs information \n" : Must start with 2 spaces and show bssid first. + // " ... \n" : Continues to print AP information and + // total AP information line should be + // same as the number of AP in the scan result. + // "Latest native pno scan results:\n": Key word can't modify. + // " ... \n": Any String and any line. No limited. + // "Latest native scan results IEs:\n": Key word can't modify. + // ie raw data : loop to start print ie raw data, finish with "\n" + // ... : Continues to print ie raw data and + // total ie raw data line should be same as + // the number of AP in the scan result. + // "\n" : Should be stop with "\n" + //--------------------------------------------- + Pattern scanResultRegex = Pattern.compile( + "" + NATIVE_SCAN_TITLE + "\n" + + " .*\n" + + "( .{2}:.{2}:.{2}:.{2}:.{2}:.{2}.*\n){" + ap_count + "}" + + "" + NATIVE_PNO_SCAN_TITLE + "\n" + + "(.*\n)*" + + "" + NATIVE_SCAN_IE_TITLE + "\n" + + "(.*\n){" + ap_count + "}" + + "\n"); + + // Trigger a scan to update mNativeScanResults in WificondScannerImpl. + assertTrue(mScanner.startSingleScan(settings, eventHandler)); + Set<Integer> expectedScan = expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ); + order.verify(mWifiNative).scan(eq(IFACE_NAME), anyInt(), eq(expectedScan), any(List.class)); + + // Notify scan has finished + mWifiMonitor.sendMessage(eq(IFACE_NAME), WifiMonitor.SCAN_RESULTS_EVENT); + mLooper.dispatchAll(); + // Get the dump string. + String dump = dumpObject(); + + assertLogContainsRequestPattern(scanResultRegex, dump); } @Test @@ -181,11 +276,9 @@ public class WificondScannerTest extends BaseWifiScannerImplTest { eq(WifiMonitor.SCAN_RESULTS_EVENT), any()); } - private void assertDumpContainsRequestLog(String log) { - String objectDump = dumpObject(); - Pattern logLineRegex = Pattern.compile(".*" + log + ".*"); - assertTrue("dump did not contain log = " + log + "\n " + objectDump + "\n", - logLineRegex.matcher(objectDump).find()); + private void assertLogContainsRequestPattern(Pattern logLineRegex, String log) { + assertTrue("dump did not contain log = " + logLineRegex.toString() + "\n" + log + "\n", + logLineRegex.matcher(log).find()); } private String dumpObject() { diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java index ee43c001a4..d02a790c45 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java @@ -26,6 +26,7 @@ import android.net.wifi.WifiScanner; import androidx.test.filters.SmallTest; import com.android.internal.util.ArrayUtils; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiNative; import org.junit.Before; @@ -40,7 +41,7 @@ import java.util.Arrays; * Unit tests for {@link com.android.server.wifi.util.ApConfigUtil}. */ @SmallTest -public class ApConfigUtilTest { +public class ApConfigUtilTest extends WifiBaseTest { private static final String TEST_COUNTRY_CODE = "TestCountry"; @@ -98,7 +99,9 @@ public class ApConfigUtilTest { 5765, 153, 5785, 157, 5805, 161, - 5825, 165 + 5825, 165, + 5845, 169, + 5865, 173 }; private static final Integer[] ALLOWED_2G_CHANNELS = {1, 2, 3, 4}; diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/BitMaskTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/BitMaskTest.java index c905f27fde..83144de957 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/BitMaskTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/BitMaskTest.java @@ -18,6 +18,8 @@ package com.android.server.wifi.util; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Assert; import org.junit.Test; @@ -25,7 +27,7 @@ import org.junit.Test; * Unit tests for {@link com.android.server.wifi.util.BitMask}. */ @SmallTest -public class BitMaskTest { +public class BitMaskTest extends WifiBaseTest { /** * Test that checkoff.testAndClear works as advertised */ diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/ByteArrayRingBufferTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/ByteArrayRingBufferTest.java index 5c397c70c9..d403213777 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/ByteArrayRingBufferTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/ByteArrayRingBufferTest.java @@ -23,13 +23,15 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; /** * Unit tests for {@link com.android.server.wifi.util.ByteArrayRingBuffer}. */ @SmallTest -public class ByteArrayRingBufferTest { +public class ByteArrayRingBufferTest extends WifiBaseTest { private static final int MAX_BYTES = 10; @Test diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java index c281b6440d..69958917a4 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java @@ -18,6 +18,8 @@ package com.android.server.wifi.util; import static org.junit.Assert.*; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Ignore; import org.junit.Test; @@ -26,7 +28,7 @@ import java.io.File; /** * Unit tests for {@link com.android.server.wifi.util.DataIntegrityChecker}. */ -public class DataIntegrityCheckerTest { +public class DataIntegrityCheckerTest extends WifiBaseTest { private static byte[] sGoodData = {1, 2, 3, 4}; private static byte[] sBadData = {5, 6, 7, 8}; diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/ExternalCallbackTrackerTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/ExternalCallbackTrackerTest.java index 94c1aee38b..4eee45233a 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/ExternalCallbackTrackerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/ExternalCallbackTrackerTest.java @@ -29,6 +29,8 @@ import android.os.RemoteException; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -39,7 +41,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for {@link com.android.server.wifi.util.ExternalCallbackTracker}. */ @SmallTest -public class ExternalCallbackTrackerTest { +public class ExternalCallbackTrackerTest extends WifiBaseTest { private static final int TEST_CALLBACK_IDENTIFIER = 56; @Mock Handler mHandler; @Mock ISoftApCallback mCallback; @@ -85,7 +87,7 @@ public class ExternalCallbackTrackerTest { public void testRemoveCallback() throws Exception { testAddCallback(); - assertTrue(mExternalCallbackTracker.remove(TEST_CALLBACK_IDENTIFIER)); + assertNotNull(mExternalCallbackTracker.remove(TEST_CALLBACK_IDENTIFIER)); assertEquals(0, mExternalCallbackTracker.getNumCallbacks()); assertTrue(mExternalCallbackTracker.getCallbacks().isEmpty()); verify(mBinder).unlinkToDeath(any(), anyInt()); @@ -99,7 +101,7 @@ public class ExternalCallbackTrackerTest { public void testRemoveCallbackFailureOnWrongIdentifier() throws Exception { testAddCallback(); - assertFalse(mExternalCallbackTracker.remove(TEST_CALLBACK_IDENTIFIER + 5)); + assertNull(mExternalCallbackTracker.remove(TEST_CALLBACK_IDENTIFIER + 5)); assertEquals(1, mExternalCallbackTracker.getNumCallbacks()); assertEquals(mCallback, mExternalCallbackTracker.getCallbacks().get(0)); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/FrameParserTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/FrameParserTest.java index 9fc23034ab..42d44c1a2b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/FrameParserTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/FrameParserTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiLoggerHal; import org.junit.Test; @@ -28,7 +29,7 @@ import org.junit.Test; * Unit tests for {@link com.android.server.wifi.util.FrameParser}. */ @SmallTest -public class FrameParserTest { +public class FrameParserTest extends WifiBaseTest { private static final byte[] TEST_EAPOL_1_OF_4_FRAME_BYTES = new byte[] { (byte) 0x7C, (byte) 0x7D, (byte) 0x3D, (byte) 0x51, (byte) 0x10, (byte) 0xDC, @@ -340,4 +341,4 @@ public class FrameParserTest { assertEquals("Action No Ack", parser.mTypeString); assertEquals("N/A", parser.mResultString); } -}
\ No newline at end of file +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java index dd567053c0..c11b300e54 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.net.wifi.ScanResult; import android.net.wifi.ScanResult.InformationElement; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.NetworkDetail; +import com.android.server.wifi.util.InformationElementUtil.HtOperation; +import com.android.server.wifi.util.InformationElementUtil.VhtOperation; import org.junit.Test; @@ -38,7 +42,7 @@ import java.util.BitSet; * Unit tests for {@link com.android.server.wifi.util.InformationElementUtil}. */ @SmallTest -public class InformationElementUtilTest { +public class InformationElementUtilTest extends WifiBaseTest { // SSID Information Element tags private static final byte[] TEST_SSID_BYTES_TAG = new byte[] { (byte) 0x00, (byte) 0x0B }; @@ -775,6 +779,24 @@ public class InformationElementUtilTest { } /** + * Test Capabilities.generateCapabilitiesString() with the IBSS capability bit set. + * + * Expect the function to return a string with [IBSS] there. + */ + @Test + public void buildCapabilities_IbssCapabilitySet() { + BitSet beaconCap = new BitSet(16); + beaconCap.set(1); + + InformationElementUtil.Capabilities capabilities = + new InformationElementUtil.Capabilities(); + capabilities.from(new InformationElement[0], beaconCap, false); + String result = capabilities.generateCapabilitiesString(); + + assertEquals("[IBSS]", result); + } + + /** * Verify the expectations when building an ExtendedCapabilites IE from data with no bits set. * Both ExtendedCapabilities#isStrictUtf8() and ExtendedCapabilites#is80211McRTTResponder() * should return false. @@ -990,5 +1012,163 @@ public class InformationElementUtilTest { assertEquals(0x112233445566L, interworking.hessid); } + /** + * Verify that the expected HT Operation information element is parsed and retrieved from the + * list of IEs. + * + * @throws Exception + */ + @Test + public void getHtOperationElement() throws Exception { + final int primaryFreq = 2467; + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_HT_OPERATION; + /** + * HT Operation Format: + * | Primary Channel | HT Operation Info | Basic HT-MCS Set | + * 1 5 16 + * + * HT Operation Info Format (relevant parts only): + * + * B0 B1 B2 ----- + * | Secondary Channel Offset | STA Channel Width | Other | + */ + ie.bytes = new byte[22]; + ie.bytes[0] = (byte) 11; + ie.bytes[1] = (byte) 0x83; //Setting Secondary channel offset = 3 + // Remaining bytes are not relevant + + HtOperation htOperation = new HtOperation(); + htOperation.from(ie); + + assertTrue(htOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_40MHZ, htOperation.getChannelWidth()); + assertEquals(primaryFreq - 10, htOperation.getCenterFreq0(primaryFreq)); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 20/40 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement20_40Mhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x00, (byte) 0xF0, (byte) 0xF1, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.UNSPECIFIED, vhtOperation.getChannelWidth()); + assertEquals(0, vhtOperation.getCenterFreq0()); + assertEquals(0, vhtOperation.getCenterFreq1()); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 80 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement80Mhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x01, (byte) 36, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_80MHZ, vhtOperation.getChannelWidth()); + assertEquals(5180, vhtOperation.getCenterFreq0()); + assertEquals(0, vhtOperation.getCenterFreq1()); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 160 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement160Mhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x01, (byte) 44, (byte) 36, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_160MHZ, vhtOperation.getChannelWidth()); + assertEquals(5220, vhtOperation.getCenterFreq0()); + assertEquals(5180, vhtOperation.getCenterFreq1()); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 80+80 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement80PlusMhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x01, (byte) 54, (byte) 36, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ, vhtOperation.getChannelWidth()); + assertEquals(5270, vhtOperation.getCenterFreq0()); + assertEquals(5180, vhtOperation.getCenterFreq1()); + } + // TODO: SAE, OWN, SUITE_B } diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/IntCounterTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/IntCounterTest.java index 794f562665..d82f07334a 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/IntCounterTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/IntCounterTest.java @@ -21,6 +21,7 @@ import static com.android.server.wifi.WifiMetricsTestUtil.buildInt32Count; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.nano.WifiMetricsProto.Int32Count; import org.junit.Test; @@ -30,7 +31,7 @@ import org.junit.Test; * Unit tests for IntCounter. */ @SmallTest -public class IntCounterTest { +public class IntCounterTest extends WifiBaseTest { private static final int[] TEST_KEYS = { 100, 20, 34, 5656, 3535, 6456, -1231, -4235, 20, 3535, -5, 100, 6456, 34, -4235, -4235 diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java index fc09036af8..888b5f2ac9 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java @@ -23,6 +23,7 @@ import static org.hamcrest.core.IsEqual.equalTo; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.nano.WifiMetricsProto.HistogramBucketInt32; import org.junit.Before; @@ -36,7 +37,7 @@ import org.mockito.MockitoAnnotations; * Unit tests for IntHistogram. */ @SmallTest -public class IntHistogramTest { +public class IntHistogramTest extends WifiBaseTest { @Rule public ErrorCollector collector = new ErrorCollector(); diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/KalmanFilterTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/KalmanFilterTest.java index 54d6e0c3e4..c5ac0452b7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/KalmanFilterTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/KalmanFilterTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.util.Random; @@ -29,7 +31,7 @@ import java.util.Random; * Unit tests for {@link com.android.server.wifi.util.KalmanFilter}. */ @SmallTest -public class KalmanFilterTest { +public class KalmanFilterTest extends WifiBaseTest { /** * Test that constructor works */ diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/MatrixTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/MatrixTest.java index 3d971ab663..5fa95be9f2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/MatrixTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/MatrixTest.java @@ -22,6 +22,8 @@ import static org.junit.Assert.fail; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.util.ArrayList; @@ -32,7 +34,7 @@ import java.util.Random; * Unit tests for {@link com.android.server.wifi.util.Matrix}. */ @SmallTest -public class MatrixTest { +public class MatrixTest extends WifiBaseTest { /** * Test that both forms of constructor work */ diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/MetricsUtilsTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/MetricsUtilsTest.java index 58d4b9e39a..e2f4d3d379 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/MetricsUtilsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/MetricsUtilsTest.java @@ -22,6 +22,8 @@ import android.util.SparseIntArray; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,7 +34,7 @@ import org.mockito.MockitoAnnotations; * Unit test harness for MetricsUtils. */ @SmallTest -public class MetricsUtilsTest { +public class MetricsUtilsTest extends WifiBaseTest { @Rule public ErrorCollector collector = new ErrorCollector(); diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java index d4bcb479e5..566a94b45c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.*; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; import java.util.ArrayList; @@ -29,7 +31,7 @@ import java.util.Arrays; * Unit tests for {@link com.android.server.wifi.util.NativeUtil}. */ @SmallTest -public class NativeUtilTest { +public class NativeUtilTest extends WifiBaseTest { /** * Test that parsing a typical colon-delimited MAC address works. */ diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/ObjectCounterTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/ObjectCounterTest.java index 20a2803532..b468a9a2b7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/ObjectCounterTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/ObjectCounterTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertThat; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.util.ObjectCounter.ProtobufConverter; import org.hamcrest.collection.IsIterableContainingInAnyOrder; @@ -34,7 +35,7 @@ import java.util.Objects; * Unit test for ObjectCounter */ @SmallTest -public class ObjectCounterTest { +public class ObjectCounterTest extends WifiBaseTest { /** * Test Key type: composite key with 3 fields diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java index 45adffd1dd..30d84de32e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java @@ -26,6 +26,7 @@ import android.net.wifi.WifiSsid; import androidx.test.filters.SmallTest; import com.android.server.wifi.ScanDetail; +import com.android.server.wifi.WifiBaseTest; import org.junit.Test; @@ -36,7 +37,7 @@ import java.util.Arrays; * Unit tests for {@link com.android.server.wifi.util.ScanResultUtil}. */ @SmallTest -public class ScanResultUtilTest { +public class ScanResultUtilTest extends WifiBaseTest { @Test public void convertScanResult() { diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/StringUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/StringUtilTest.java index 7308f12272..db61686991 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/StringUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/StringUtilTest.java @@ -21,13 +21,15 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; +import com.android.server.wifi.WifiBaseTest; + import org.junit.Test; /** * Unit tests for {@link com.android.server.wifi.util.StringUtil}. */ @SmallTest -public class StringUtilTest { +public class StringUtilTest extends WifiBaseTest { static final byte ASCII_UNIT_SEPARATOR = 31; static final byte ASCII_DEL = 127; diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java index 531673f0ed..483855a16b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.server.wifi.CarrierNetworkConfig; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiConfigurationTestUtil; import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData; import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData; @@ -49,7 +50,7 @@ import javax.crypto.Cipher; * Unit tests for {@link com.android.server.wifi.util.TelephonyUtil}. */ @SmallTest -public class TelephonyUtilTest { +public class TelephonyUtilTest extends WifiBaseTest { private TelephonyUtil mTelephonyUtil; @Mock diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/TimedQuotaManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/TimedQuotaManagerTest.java index 3dcad7cd70..4884ac9278 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/TimedQuotaManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/TimedQuotaManagerTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import androidx.test.filters.SmallTest; import com.android.server.wifi.Clock; +import com.android.server.wifi.WifiBaseTest; import org.junit.Before; import org.junit.Test; @@ -35,7 +36,7 @@ import java.time.Duration; * Unit tests for {@link TimedQuotaManager}. */ @SmallTest -public class TimedQuotaManagerTest { +public class TimedQuotaManagerTest extends WifiBaseTest { private static final long DAY_MILLIS = Duration.ofDays(1).toMillis(); diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/WifiHandlerTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/WifiHandlerTest.java index fbf147180c..edbe39df32 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/WifiHandlerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/WifiHandlerTest.java @@ -25,6 +25,7 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; import com.android.server.wifi.FakeWifiLog; +import com.android.server.wifi.WifiBaseTest; import org.junit.Before; import org.junit.Test; @@ -36,7 +37,7 @@ import org.mockito.Spy; /** Unit tests for {@link WifiHandler}. */ @RunWith(JUnit4.class) @SmallTest -public class WifiHandlerTest { +public class WifiHandlerTest extends WifiBaseTest { private static final String TAG = "WifiHandlerTest"; private WifiHandler mCodeUnderTest; @Spy FakeWifiLog mWifiLog; diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java index 0c9ed26b7b..8e3c1bb713 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java @@ -31,15 +31,15 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.app.AppOpsManager; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; 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.location.LocationManager; import android.net.NetworkStack; import android.os.Build; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -47,6 +47,7 @@ import androidx.test.filters.SmallTest; import com.android.server.wifi.BinderUtil; import com.android.server.wifi.FakeWifiLog; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiInjector; import org.junit.Before; @@ -59,13 +60,12 @@ 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) @SmallTest -public class WifiPermissionsUtilTest { +public class WifiPermissionsUtilTest extends WifiBaseTest { public static final String TAG = "WifiPermissionsUtilTest"; // Mock objects for testing @@ -74,13 +74,14 @@ public class WifiPermissionsUtilTest { @Mock private PackageManager mMockPkgMgr; @Mock private ApplicationInfo mMockApplInfo; @Mock private AppOpsManager mMockAppOps; - @Mock private UserInfo mMockUserInfo; @Mock private UserManager mMockUserManager; @Mock private ContentResolver mMockContentResolver; @Mock private WifiInjector mWifiInjector; @Mock private LocationManager mLocationManager; + @Mock private DevicePolicyManager mDevicePolicyManager; @Spy private FakeWifiLog mWifiLog; + private static final String TEST_WIFI_STACK_APK_NAME = "com.android.wifi"; 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; @@ -90,7 +91,6 @@ public class WifiPermissionsUtilTest { private static final boolean DONT_HIDE_FROM_APP_OPS = false; private static final boolean HIDE_FROM_APP_OPS = true; - 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"; @@ -109,7 +109,6 @@ public class WifiPermissionsUtilTest { private int mFineLocationPermission; private int mAllowFineLocationApps; private int mHardwareLocationPermission; - private String mPkgNameOfTopActivity; private int mCurrentUser; private boolean mIsLocationEnabled; private boolean mThrowSecurityException; @@ -159,20 +158,6 @@ public class WifiPermissionsUtilTest { } /** - * 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, mMockUserManager, mWifiInjector); - doThrow(new RemoteException("Failed to check permissions for " + mUid)) - .when(mMockPermissionsWrapper).getOverrideWifiConfigPermission(mUid); - assertFalse(codeUnderTest.checkConfigOverridePermission(mUid)); - } - - /** * Test case setting: Package is valid * Location mode is enabled * Caller can read peers mac address @@ -187,7 +172,7 @@ public class WifiPermissionsUtilTest { mUid = MANAGED_PROFILE_UID; mPermissionsList.put(mMacAddressPermission, mUid); mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; - mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mCurrentUser = UserHandle.USER_SYSTEM; mIsLocationEnabled = true; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, @@ -210,7 +195,6 @@ public class WifiPermissionsUtilTest { mUid = MANAGED_PROFILE_UID; mPermissionsList.put(mMacAddressPermission, mUid); mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; - mMockUserInfo.id = mCallingUser; mIsLocationEnabled = true; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, @@ -297,10 +281,9 @@ public class WifiPermissionsUtilTest { public void testLegacyForegroundAppWithOtherPermissionsDenied() throws Exception { 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; + mCurrentUser = UserHandle.USER_SYSTEM; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); @@ -328,7 +311,6 @@ public class WifiPermissionsUtilTest { mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; mUid = MANAGED_PROFILE_UID; - mMockUserInfo.id = mCallingUser; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); @@ -377,8 +359,6 @@ public class WifiPermissionsUtilTest { mIsLocationEnabled = false; setupTestCase(); - when(mMockPermissionsWrapper.getChangeWifiConfigPermission(mUid)) - .thenReturn(PackageManager.PERMISSION_DENIED); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); @@ -411,8 +391,6 @@ public class WifiPermissionsUtilTest { mIsLocationEnabled = false; setupTestCase(); - when(mMockPermissionsWrapper.getChangeWifiConfigPermission(mUid)) - .thenReturn(PackageManager.PERMISSION_GRANTED); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); @@ -446,8 +424,6 @@ public class WifiPermissionsUtilTest { mIsLocationEnabled = false; setupTestCase(); - when(mMockPermissionsWrapper.getAccessWifiStatePermission(mUid)) - .thenReturn(PackageManager.PERMISSION_GRANTED); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); @@ -719,14 +695,14 @@ public class WifiPermissionsUtilTest { mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; mUid = MANAGED_PROFILE_UID; - mMockUserInfo.id = mCallingUser; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); codeUnderTest.enforceLocationPermission(TEST_PACKAGE_NAME, mUid); // verify that checking FINE for legacy apps! - verify(mMockAppOps).noteOp(eq(AppOpsManager.OP_FINE_LOCATION), anyInt(), anyString()); + verify(mMockAppOps).noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), anyInt(), anyString(), + any(), any()); } /** @@ -742,12 +718,12 @@ public class WifiPermissionsUtilTest { mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED; mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; mUid = MANAGED_PROFILE_UID; - mMockUserInfo.id = mCallingUser; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); codeUnderTest.enforceLocationPermission(TEST_PACKAGE_NAME, mUid); - verify(mMockAppOps).noteOp(eq(AppOpsManager.OP_FINE_LOCATION), anyInt(), anyString()); + verify(mMockAppOps) + .noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), anyInt(), anyString(), any(), any()); } /** @@ -765,7 +741,6 @@ public class WifiPermissionsUtilTest { mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; mUid = MANAGED_PROFILE_UID; - mMockUserInfo.id = mCallingUser; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); @@ -829,8 +804,8 @@ public class WifiPermissionsUtilTest { WifiPermissionsUtil wifiPermissionsUtil = new WifiPermissionsUtil(mMockPermissionsWrapper, mMockContext, mMockUserManager, mWifiInjector); - when(mMockAppOps.noteOp( - AppOpsManager.OP_SYSTEM_ALERT_WINDOW, MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)) + when(mMockAppOps.noteOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, MANAGED_PROFILE_UID, + TEST_PACKAGE_NAME, null, null)) .thenReturn(AppOpsManager.MODE_DEFAULT); when(mMockPermissionsWrapper.getUidPermission( Manifest.permission.SYSTEM_ALERT_WINDOW, MANAGED_PROFILE_UID)) @@ -838,8 +813,8 @@ public class WifiPermissionsUtilTest { assertFalse(wifiPermissionsUtil.checkSystemAlertWindowPermission( MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); - when(mMockAppOps.noteOp( - AppOpsManager.OP_SYSTEM_ALERT_WINDOW, MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)) + when(mMockAppOps.noteOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, MANAGED_PROFILE_UID, + TEST_PACKAGE_NAME, null, null)) .thenReturn(AppOpsManager.MODE_DEFAULT); when(mMockPermissionsWrapper.getUidPermission( Manifest.permission.SYSTEM_ALERT_WINDOW, MANAGED_PROFILE_UID)) @@ -865,6 +840,95 @@ public class WifiPermissionsUtilTest { } /** + * Verifies the helper method exposed for checking if the app is a DeviceOwner. + */ + @Test + public void testIsDeviceOwnerApp() throws Exception { + setupMocks(); + WifiPermissionsUtil wifiPermissionsUtil = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockUserManager, mWifiInjector); + + when(mMockContext.getSystemService(DevicePolicyManager.class)) + .thenReturn(mDevicePolicyManager); + + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(new ComponentName(TEST_PACKAGE_NAME, new String())); + when(mDevicePolicyManager.getDeviceOwnerUser()) + .thenReturn(UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)); + assertTrue(wifiPermissionsUtil.isDeviceOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + + + // userId does not match + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(new ComponentName(TEST_PACKAGE_NAME, new String())); + when(mDevicePolicyManager.getDeviceOwnerUser()) + .thenReturn(UserHandle.getUserHandleForUid(OTHER_USER_UID)); + assertFalse(wifiPermissionsUtil.isDeviceOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + + // Package Name does not match + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(new ComponentName(INVALID_PACKAGE, new String())); + when(mDevicePolicyManager.getDeviceOwnerUser()) + .thenReturn(UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)); + assertFalse(wifiPermissionsUtil.isDeviceOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + + // No device owner. + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(null); + assertFalse(wifiPermissionsUtil.isDeviceOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + + // DevicePolicyManager does not exist. + when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) + .thenReturn(null); + assertFalse(wifiPermissionsUtil.isDeviceOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + } + + /** + * Verifies the helper method exposed for checking if the app is a ProfileOwner. + */ + @Test + public void testIsProfileOwnerApp() throws Exception { + setupMocks(); + WifiPermissionsUtil wifiPermissionsUtil = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockUserManager, mWifiInjector); + + when(mMockContext.createPackageContextAsUser( + TEST_WIFI_STACK_APK_NAME, 0, UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID))) + .thenReturn(mMockContext); + when(mMockContext.getSystemService(DevicePolicyManager.class)) + .thenReturn(mDevicePolicyManager); + + when(mDevicePolicyManager.isProfileOwnerApp(TEST_PACKAGE_NAME)) + .thenReturn(true); + assertTrue(wifiPermissionsUtil.isProfileOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + + when(mDevicePolicyManager.isProfileOwnerApp(TEST_PACKAGE_NAME)) + .thenReturn(false); + assertFalse(wifiPermissionsUtil.isProfileOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + + // DevicePolicyManager does not exist. + when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) + .thenReturn(null); + assertFalse(wifiPermissionsUtil.isProfileOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + + // Invalid package name. + doThrow(new PackageManager.NameNotFoundException()) + .when(mMockContext).createPackageContextAsUser( + TEST_WIFI_STACK_APK_NAME, 0, + UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)); + assertFalse(wifiPermissionsUtil.isProfileOwner( + MANAGED_PROFILE_UID, TEST_PACKAGE_NAME)); + } + + /** * Test case setting: caller does not have Location permission. * Expect a SecurityException */ @@ -899,9 +963,10 @@ public class WifiPermissionsUtilTest { codeUnderTest.enforceCanAccessScanResultsForWifiScanner(TEST_PACKAGE_NAME, mUid, CHECK_LOCATION_SETTINGS, DONT_HIDE_FROM_APP_OPS); - verify(mMockAppOps, never()).checkOp( - AppOpsManager.OP_FINE_LOCATION, mUid, TEST_PACKAGE_NAME); - verify(mMockAppOps).noteOp(AppOpsManager.OP_FINE_LOCATION, mUid, TEST_PACKAGE_NAME); + verify(mMockAppOps, never()) + .unsafeCheckOp(AppOpsManager.OPSTR_FINE_LOCATION, mUid, TEST_PACKAGE_NAME); + verify(mMockAppOps) + .noteOp(AppOpsManager.OPSTR_FINE_LOCATION, mUid, TEST_PACKAGE_NAME, null, null); } /** @@ -928,9 +993,10 @@ public class WifiPermissionsUtilTest { codeUnderTest.enforceCanAccessScanResultsForWifiScanner(TEST_PACKAGE_NAME, mUid, IGNORE_LOCATION_SETTINGS, HIDE_FROM_APP_OPS); - verify(mMockAppOps).checkOp(AppOpsManager.OP_FINE_LOCATION, mUid, TEST_PACKAGE_NAME); + verify(mMockAppOps).unsafeCheckOp(AppOpsManager.OPSTR_FINE_LOCATION, mUid, + TEST_PACKAGE_NAME); verify(mMockAppOps, never()).noteOp( - AppOpsManager.OP_FINE_LOCATION, mUid, TEST_PACKAGE_NAME); + AppOpsManager.OPSTR_FINE_LOCATION, mUid, TEST_PACKAGE_NAME, null, null); } @@ -1148,16 +1214,16 @@ public class WifiPermissionsUtilTest { } private void setupMocks() throws Exception { - when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PACKAGE_NAME), eq(0), anyInt())) + when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PACKAGE_NAME), eq(0), any())) .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); - when(mMockAppOps.noteOp(AppOpsManager.OP_FINE_LOCATION, mUid, TEST_PACKAGE_NAME)) - .thenReturn(mAllowFineLocationApps); - when(mMockAppOps.checkOp(AppOpsManager.OP_FINE_LOCATION, mUid, TEST_PACKAGE_NAME)) + when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PACKAGE_NAME, null, null)) + .thenReturn(mWifiScanAllowApps); + when(mMockAppOps.noteOp(AppOpsManager.OPSTR_COARSE_LOCATION, mUid, TEST_PACKAGE_NAME, null, + null)).thenReturn(mAllowCoarseLocationApps); + when(mMockAppOps.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, mUid, TEST_PACKAGE_NAME, null, + null)).thenReturn(mAllowFineLocationApps); + when(mMockAppOps.unsafeCheckOp(AppOpsManager.OPSTR_FINE_LOCATION, mUid, TEST_PACKAGE_NAME)) .thenReturn(mAllowFineLocationApps); if (mThrowSecurityException) { doThrow(new SecurityException("Package " + TEST_PACKAGE_NAME + " doesn't belong" @@ -1166,13 +1232,12 @@ public class WifiPermissionsUtilTest { } 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); when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager); + when(mMockContext.getPackageName()).thenReturn(TEST_WIFI_STACK_APK_NAME); } private void initTestVars() { @@ -1181,9 +1246,7 @@ public class WifiPermissionsUtilTest { mWifiScanAllowApps = AppOpsManager.MODE_ERRORED; mUid = OTHER_USER_UID; mThrowSecurityException = true; - mMockUserInfo.id = UserHandle.USER_NULL; mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M; - mPkgNameOfTopActivity = INVALID_PACKAGE; mIsLocationEnabled = false; mCurrentUser = UserHandle.USER_SYSTEM; mCoarseLocationPermission = PackageManager.PERMISSION_DENIED; @@ -1198,7 +1261,9 @@ public class WifiPermissionsUtilTest { anyString(), anyInt()); doAnswer(mReturnPermission).when(mMockPermissionsWrapper).getUidPermission( anyString(), anyInt()); - when(mMockPermissionsWrapper.getCallingUserId(mUid)).thenReturn(mCallingUser); + when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM, + UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID))) + .thenReturn(true); when(mMockPermissionsWrapper.getCurrentUser()).thenReturn(mCurrentUser); when(mMockPermissionsWrapper.getUidPermission(mManifestStringCoarse, mUid)) .thenReturn(mCoarseLocationPermission); @@ -1207,6 +1272,5 @@ public class WifiPermissionsUtilTest { when(mMockPermissionsWrapper.getUidPermission(mManifestStringHardware, mUid)) .thenReturn(mHardwareLocationPermission); when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled); - when(mMockPermissionsWrapper.getTopPkgName()).thenReturn(mPkgNameOfTopActivity); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java index 85b4a93700..e2a3411872 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.net.IpConfiguration; +import android.net.MacAddress; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.WifiEnterpriseConfig; @@ -29,6 +30,7 @@ import android.util.Xml; import androidx.test.filters.SmallTest; import com.android.internal.util.FastXmlSerializer; +import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiConfigurationTestUtil; import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil; import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil; @@ -50,7 +52,7 @@ import java.util.HashMap; * Unit tests for {@link com.android.server.wifi.util.XmlUtil}. */ @SmallTest -public class XmlUtilTest { +public class XmlUtilTest extends WifiBaseTest { public static final String XML_STRING_EAP_METHOD_REPLACE_FORMAT = "<int name=\"EapMethod\" value=\"%d\" />"; @@ -208,7 +210,7 @@ public class XmlUtilTest { configuration.lastUpdateUid = configuration.lastConnectUid = configuration.creatorUid; configuration.creatorName = configuration.lastUpdateName = TEST_PACKAGE_NAME; configuration.creationTime = "04-04-2016"; - configuration.getOrCreateRandomizedMacAddress(); + configuration.setRandomizedMacAddress(MacAddress.createRandomUnicastAddress()); configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; serializeDeserializeWifiConfigurationForConfigStore(configuration); diff --git a/service/tests/wifitests/src/com/android/server/wifi/wificond/NativeScanResultTest.java b/service/tests/wifitests/src/com/android/server/wifi/wificond/NativeScanResultTest.java deleted file mode 100644 index 11be8315f9..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/wificond/NativeScanResultTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 androidx.test.filters.SmallTest; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -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; - private static final int[] RADIO_CHAIN_IDS = { 0, 1 }; - private static final int[] RADIO_CHAIN_LEVELS = { -56, -65 }; - - /** - * NativeScanResult object can be serialized and deserialized, while keeping the - * values unchanged. - */ - @Test - public void canSerializeAndDeserialize() { - 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; - scanResult.radioChainInfos = new ArrayList<>(Arrays.asList( - new RadioChainInfo(RADIO_CHAIN_IDS[0], RADIO_CHAIN_LEVELS[0]), - new RadioChainInfo(RADIO_CHAIN_IDS[1], RADIO_CHAIN_LEVELS[1]))); - 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); - assertEquals(scanResult.capability, scanResultDeserialized.capability); - assertEquals(scanResult.associated, scanResultDeserialized.associated); - assertTrue(scanResult.radioChainInfos.containsAll(scanResultDeserialized.radioChainInfos)); - } -} diff --git a/service/tests/wifitests/src/com/android/server/wifi/wificond/PnoSettingsTest.java b/service/tests/wifitests/src/com/android/server/wifi/wificond/PnoSettingsTest.java deleted file mode 100644 index b484665c39..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/wificond/PnoSettingsTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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 android.os.Parcel; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; - -/** - * 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_FREQUENCIES_1 = {}; - private static final int[] TEST_FREQUENCIES_2 = {2500, 5124}; - 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; - private static final int TEST_VALUE = 42; - - private PnoNetwork mPnoNetwork1; - private PnoNetwork mPnoNetwork2; - - @Before - public void setUp() { - mPnoNetwork1 = new PnoNetwork(); - mPnoNetwork1.ssid = TEST_SSID_1; - mPnoNetwork1.isHidden = true; - mPnoNetwork1.frequencies = TEST_FREQUENCIES_1; - - mPnoNetwork2 = new PnoNetwork(); - mPnoNetwork2.ssid = TEST_SSID_2; - mPnoNetwork2.isHidden = false; - mPnoNetwork2.frequencies = TEST_FREQUENCIES_2; - } - - /** - * PnoSettings object can be serialized and deserialized, while keeping the - * values unchanged. - */ - @Test - public void canSerializeAndDeserialize() { - PnoSettings pnoSettings = new PnoSettings(); - pnoSettings.intervalMs = TEST_INTERVAL_MS; - pnoSettings.min2gRssi = TEST_MIN_2G_RSSI; - pnoSettings.min5gRssi = TEST_MIN_5G_RSSI; - pnoSettings.pnoNetworks = new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2)); - - 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); - - assertEquals(pnoSettings, pnoSettingsDeserialized); - assertEquals(pnoSettings.hashCode(), pnoSettingsDeserialized.hashCode()); - } - - /** - * Tests usage of {@link PnoSettings} as a HashMap key type. - */ - @Test - public void testAsHashMapKey() { - PnoSettings pnoSettings1 = new PnoSettings(); - pnoSettings1.intervalMs = TEST_INTERVAL_MS; - pnoSettings1.min2gRssi = TEST_MIN_2G_RSSI; - pnoSettings1.min5gRssi = TEST_MIN_5G_RSSI; - pnoSettings1.pnoNetworks = new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2)); - - PnoSettings pnoSettings2 = new PnoSettings(); - pnoSettings2.intervalMs = TEST_INTERVAL_MS; - pnoSettings2.min2gRssi = TEST_MIN_2G_RSSI; - pnoSettings2.min5gRssi = TEST_MIN_5G_RSSI; - pnoSettings2.pnoNetworks = new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2)); - - assertEquals(pnoSettings1, pnoSettings2); - assertEquals(pnoSettings1.hashCode(), pnoSettings2.hashCode()); - - HashMap<PnoSettings, Integer> map = new HashMap<>(); - map.put(pnoSettings1, TEST_VALUE); - - assertEquals(TEST_VALUE, map.get(pnoSettings2).intValue()); - } -} diff --git a/service/tests/wifitests/src/com/android/server/wifi/wificond/SingleScanSettingsTest.java b/service/tests/wifitests/src/com/android/server/wifi/wificond/SingleScanSettingsTest.java deleted file mode 100644 index 5b424b7682..0000000000 --- a/service/tests/wifitests/src/com/android/server/wifi/wificond/SingleScanSettingsTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 android.net.wifi.IWifiScannerImpl; -import android.os.Parcel; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; - -/** - * 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; - private static final int TEST_VALUE = 42; - - private ChannelSettings mChannelSettings1; - private ChannelSettings mChannelSettings2; - private HiddenNetwork mHiddenNetwork1; - private HiddenNetwork mHiddenNetwork2; - - @Before - public void setUp() { - mChannelSettings1 = new ChannelSettings(); - mChannelSettings1.frequency = TEST_FREQUENCY_1; - mChannelSettings2 = new ChannelSettings(); - mChannelSettings2.frequency = TEST_FREQUENCY_2; - - mHiddenNetwork1 = new HiddenNetwork(); - mHiddenNetwork1.ssid = TEST_SSID_1; - mHiddenNetwork2 = new HiddenNetwork(); - mHiddenNetwork2.ssid = TEST_SSID_2; - } - - /** - * SingleScanSettings object can be serialized and deserialized, while keeping the - * values unchanged. - */ - @Test - public void canSerializeAndDeserialize() { - SingleScanSettings scanSettings = new SingleScanSettings(); - scanSettings.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; - - scanSettings.channelSettings = - new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2)); - scanSettings.hiddenNetworks = - new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2)); - - 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); - - assertEquals(scanSettings, scanSettingsDeserialized); - assertEquals(scanSettings.hashCode(), scanSettingsDeserialized.hashCode()); - } - - /** - * Tests usage of {@link SingleScanSettings} as a HashMap key type. - */ - @Test - public void testAsHashMapKey() { - SingleScanSettings scanSettings1 = new SingleScanSettings(); - scanSettings1.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; - scanSettings1.channelSettings = - new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2)); - scanSettings1.hiddenNetworks = - new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2)); - - SingleScanSettings scanSettings2 = new SingleScanSettings(); - scanSettings2.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; - scanSettings2.channelSettings = - new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2)); - scanSettings2.hiddenNetworks = - new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2)); - - assertEquals(scanSettings1, scanSettings2); - assertEquals(scanSettings1.hashCode(), scanSettings2.hashCode()); - - HashMap<SingleScanSettings, Integer> map = new HashMap<>(); - map.put(scanSettings1, TEST_VALUE); - - assertEquals(TEST_VALUE, map.get(scanSettings2).intValue()); - } -} diff --git a/service/wifi.rc b/service/wifi.rc new file mode 100644 index 0000000000..9a04252dc5 --- /dev/null +++ b/service/wifi.rc @@ -0,0 +1,84 @@ +# +# 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. +# + +# These are needed for migration of data from "system" user to "network_stack" user +# since wifi is no longer running in "system_server". +on post-fs-data + chown network_stack network_stack /data/misc/wifi + chown network_stack network_stack /data/misc/wifi/WifiConfigStore.xml + chown network_stack network_stack /data/misc/wifi/softap.conf + +on property:sys.user.0.ce_available=true + mkdir /data/misc_ce/0/wifi 0770 network_stack network_stack + # For devices upgrading, we need to change permission. + chown network_stack network_stack /data/misc_ce/0/wifi + chown network_stack network_stack /data/misc_ce/0/wifi/WifiConfigStore.xml + chown network_stack network_stack /data/misc_ce/0/wifi/WifiConfigStoreNetworkSuggestions.xml + + # Load the new sepolicy file context labels (these files were relabeled in R). + restorecon /data/misc_ce/0/wifi + restorecon /data/misc_ce/0/wifi/WifiConfigStore.xml + restorecon /data/misc_ce/0/wifi/WifiConfigStoreNetworkSuggestions.xml + +# Below are for kernel tracing related stuff. +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 network_stack to enable/disable, and read wifi trace + # events. + chown network_stack network_stack /sys/kernel/debug/tracing/instances/wifi/tracing_on + chown network_stack network_stack /sys/kernel/debug/tracing/instances/wifi/free_buffer + chown network_stack network_stack /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=* && property:sys.wifitracing.started=1 + # Override default value. + write /sys/kernel/debug/tracing/instances/wifi/events/net/filter name==${wifi.interface} diff --git a/service/wifi-events.rc b/service/wifi_inprocess.rc index f332226658..286d18063d 100644 --- a/service/wifi-events.rc +++ b/service/wifi_inprocess.rc @@ -14,6 +14,26 @@ # limitations under the License. # +# These are needed for migration of data from "network_stack" user to "system" user +# if wifi is no longer running in "system_server". +on post-fs-data + chown system system /data/misc/wifi + chown system system /data/misc/wifi/WifiConfigStore.xml + chown system system /data/misc/wifi/softap.conf + +on property:sys.user.0.ce_available=true + mkdir /data/misc_ce/0/wifi 0770 system system + # For devices upgrading, we need to change permission. + chown system system /data/misc_ce/0/wifi + chown system system /data/misc_ce/0/wifi/WifiConfigStore.xml + chown system system /data/misc_ce/0/wifi/WifiConfigStoreNetworkSuggestions.xml + + # Load the new sepolicy file context labels (these files were relabeled in R). + restorecon /data/misc_ce/0/wifi + restorecon /data/misc_ce/0/wifi/WifiConfigStore.xml + restorecon /data/misc_ce/0/wifi/WifiConfigStoreNetworkSuggestions.xml + +# Below are for kernel tracing related stuff. on fs setprop sys.wifitracing.started 0 @@ -49,7 +69,7 @@ on property:sys.boot_completed=1 && property:sys.wifitracing.started=0 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 + # Set DAC to allow system 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 |