diff options
author | Xin Li <delphij@google.com> | 2017-11-10 14:39:24 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2017-11-10 14:39:24 -0800 |
commit | 45574b17b4443c13f70c7c62b2bcb0c0ef2969d4 (patch) | |
tree | e45bf40318808b7ce83e3536c7e351d1021dc635 | |
parent | 091e23fb0c93886b83335fac9596330a4ae06804 (diff) | |
parent | 456a25e50b2fa4c85f14b74801edf6d84b650b20 (diff) | |
download | wifi-45574b17b4443c13f70c7c62b2bcb0c0ef2969d4.tar.gz |
Merge commit '456a25e50b2fa4c85f14b74801edf6d84b650b20' from
oc-mr1-dev-plus-aosp-without-vendor into stage-aosp-master.
Change-Id: I55c4a4a736e1cdbfd579bd0529ed8d8966eb9e2a
76 files changed, 6359 insertions, 3593 deletions
diff --git a/libwifi_system/Android.bp b/libwifi_system/Android.bp index 877d05f1d..f5ea184f3 100644 --- a/libwifi_system/Android.bp +++ b/libwifi_system/Android.bp @@ -29,7 +29,7 @@ cc_defaults { // Device independent wifi system logic. // ============================================================ -cc_library_shared { +cc_library { name: "libwifi-system", defaults: ["libwifi-system-defaults"], export_include_dirs: ["include"], diff --git a/libwifi_system/tests/hostapd_manager_unittest.cpp b/libwifi_system/tests/hostapd_manager_unittest.cpp index 048d4732d..b25dd8ec7 100644 --- a/libwifi_system/tests/hostapd_manager_unittest.cpp +++ b/libwifi_system/tests/hostapd_manager_unittest.cpp @@ -24,6 +24,8 @@ using std::string; using std::vector; +using testing::HasSubstr; +using testing::Not; namespace android { namespace wifi_system { @@ -34,46 +36,10 @@ const char kTestSsidStr[] = "helloisitme"; const char kTestPassphraseStr[] = "yourelookingfor"; const int kTestChannel = 2; -#define CONFIG_COMMON_PREFIX \ - "interface=foobar0\n" \ - "driver=nl80211\n" \ - "ctrl_interface=/data/misc/wifi/hostapd/ctrl\n" \ - "ssid2=68656c6c6f" "6973" "6974" "6d65\n" \ - "channel=2\n" \ - "ieee80211n=1\n" \ - "hw_mode=g\n" - // If you generate your config file with both the test ssid // and the test passphrase, you'll get this line in the config. -#define CONFIG_PSK_LINE \ - "wpa_psk=dffa36815281e5a6eca1910f254717fa2528681335e3bbec5966d2aa9221a66e\n" - -#define CONFIG_WPA_SUFFIX \ - "wpa=3\n" \ - "wpa_pairwise=TKIP CCMP\n" \ - CONFIG_PSK_LINE - -#define CONFIG_WPA2_SUFFIX \ - "wpa=2\n" \ - "rsn_pairwise=CCMP\n" \ - CONFIG_PSK_LINE - -const char kExpectedOpenConfig[] = - CONFIG_COMMON_PREFIX - "ignore_broadcast_ssid=0\n" - "wowlan_triggers=any\n"; - -const char kExpectedWpaConfig[] = - CONFIG_COMMON_PREFIX - "ignore_broadcast_ssid=0\n" - "wowlan_triggers=any\n" - CONFIG_WPA_SUFFIX; - -const char kExpectedWpa2Config[] = - CONFIG_COMMON_PREFIX - "ignore_broadcast_ssid=0\n" - "wowlan_triggers=any\n" - CONFIG_WPA2_SUFFIX; +const char kConfigPskLine[] = + "wpa_psk=dffa36815281e5a6eca1910f254717fa2528681335e3bbec5966d2aa9221a66e\n"; class HostapdManagerTest : public ::testing::Test { protected: @@ -93,27 +59,44 @@ class HostapdManagerTest : public ::testing::Test { } }; // class HostapdManagerTest +// This is used to verify config string generated by test helper function +// |GetConfigForEncryptionType|. +void VerifyCommonConfigs(const string& config) { + EXPECT_THAT(config, HasSubstr("interface=foobar0\n")); + EXPECT_THAT(config, HasSubstr("driver=nl80211\n")); + EXPECT_THAT(config, HasSubstr("ctrl_interface=/data/misc/wifi/hostapd/ctrl\n")); + EXPECT_THAT(config, HasSubstr("ssid2=68656c6c6f" "6973" "6974" "6d65\n")); + EXPECT_THAT(config, HasSubstr("channel=2\n")); + EXPECT_THAT(config, HasSubstr("hw_mode=g\n")); + EXPECT_THAT(config, HasSubstr("wowlan_triggers=any\n")); + EXPECT_THAT(config, HasSubstr("ignore_broadcast_ssid=0\n")); + EXPECT_THAT(config, Not(HasSubstr("ignore_broadcast_ssid=1\n"))); +} + } // namespace TEST_F(HostapdManagerTest, GeneratesCorrectOpenConfig) { string config = GetConfigForEncryptionType( HostapdManager::EncryptionType::kOpen); - EXPECT_FALSE(config.empty()); - EXPECT_EQ(kExpectedOpenConfig, config); + VerifyCommonConfigs(config); } TEST_F(HostapdManagerTest, GeneratesCorrectWpaConfig) { string config = GetConfigForEncryptionType( HostapdManager::EncryptionType::kWpa); - EXPECT_FALSE(config.empty()); - EXPECT_EQ(kExpectedWpaConfig, config); + VerifyCommonConfigs(config); + EXPECT_THAT(config, HasSubstr("wpa=3\n")); + EXPECT_THAT(config, HasSubstr("wpa_pairwise=TKIP CCMP\n")); + EXPECT_THAT(config, HasSubstr(kConfigPskLine)); } TEST_F(HostapdManagerTest, GeneratesCorrectWpa2Config) { string config = GetConfigForEncryptionType( HostapdManager::EncryptionType::kWpa2); - EXPECT_FALSE(config.empty()); - EXPECT_EQ(kExpectedWpa2Config, config); + VerifyCommonConfigs(config); + EXPECT_THAT(config, HasSubstr("wpa=2\n")); + EXPECT_THAT(config, HasSubstr("rsn_pairwise=CCMP\n")); + EXPECT_THAT(config, HasSubstr(kConfigPskLine)); } TEST_F(HostapdManagerTest, RespectsHiddenSetting) { @@ -124,8 +107,8 @@ TEST_F(HostapdManagerTest, RespectsHiddenSetting) { kTestChannel, HostapdManager::EncryptionType::kOpen, vector<uint8_t>()); - EXPECT_FALSE(config.find("ignore_broadcast_ssid=1\n") == string::npos); - EXPECT_TRUE(config.find("ignore_broadcast_ssid=0\n") == string::npos); + EXPECT_THAT(config, HasSubstr("ignore_broadcast_ssid=1\n")); + EXPECT_THAT(config, Not(HasSubstr("ignore_broadcast_ssid=0\n"))); } TEST_F(HostapdManagerTest, CorrectlyInfersHwMode) { @@ -136,8 +119,8 @@ TEST_F(HostapdManagerTest, CorrectlyInfersHwMode) { 44, HostapdManager::EncryptionType::kOpen, vector<uint8_t>()); - EXPECT_FALSE(config.find("hw_mode=a\n") == string::npos); - EXPECT_TRUE(config.find("hw_mode=g\n") == string::npos); + EXPECT_THAT(config, HasSubstr("hw_mode=a\n")); + EXPECT_THAT(config, Not(HasSubstr("hw_mode=g\n"))); } diff --git a/libwifi_system_iface/Android.bp b/libwifi_system_iface/Android.bp index 4be0aa01c..80249ef3c 100644 --- a/libwifi_system_iface/Android.bp +++ b/libwifi_system_iface/Android.bp @@ -26,9 +26,12 @@ wifi_system_iface_cflags = [ // Device independent wifi system logic. // ============================================================ -cc_library_shared { +cc_library { name: "libwifi-system-iface", vendor_available: true, + vndk: { + enabled: true, + }, cflags: wifi_system_iface_cflags, local_include_dirs: ["include"], export_include_dirs: ["include"], diff --git a/service/Android.mk b/service/Android.mk index 155f5b295..5b539e5de 100644 --- a/service/Android.mk +++ b/service/Android.mk @@ -70,6 +70,11 @@ 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 + + include $(BUILD_JAVA_LIBRARY) endif # !TARGET_BUILD_PDK diff --git a/service/java/com/android/server/wifi/CarrierNetworkConfig.java b/service/java/com/android/server/wifi/CarrierNetworkConfig.java new file mode 100644 index 000000000..c23213e8a --- /dev/null +++ b/service/java/com/android/server/wifi/CarrierNetworkConfig.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.wifi.EAPConstants; +import android.net.wifi.WifiEnterpriseConfig; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.util.Base64; +import android.util.Log; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Class for maintaining/caching carrier Wi-Fi network configurations. + */ +public class CarrierNetworkConfig { + private static final String TAG = "CarrierNetworkConfig"; + + private static final String NETWORK_CONFIG_SEPARATOR = ","; + private static final int ENCODED_SSID_INDEX = 0; + private static final int EAP_TYPE_INDEX = 1; + private static final int CONFIG_ELEMENT_SIZE = 2; + + private final Map<String, NetworkInfo> mCarrierNetworkMap; + + public CarrierNetworkConfig(Context context) { + mCarrierNetworkMap = new HashMap<>(); + updateNetworkConfig(context); + + // Monitor for carrier config changes. + IntentFilter filter = new IntentFilter(); + filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateNetworkConfig(context); + } + }, filter); + } + + /** + * @return true if the given SSID is associated with a carrier network + */ + public boolean isCarrierNetwork(String ssid) { + return mCarrierNetworkMap.containsKey(ssid); + } + + /** + * @return the EAP type associated with a carrier AP, or -1 if the specified AP + * is not associated with a carrier network + */ + public int getNetworkEapType(String ssid) { + NetworkInfo info = mCarrierNetworkMap.get(ssid); + return info == null ? -1 : info.mEapType; + } + + /** + * @return the name of carrier associated with a carrier AP, or null if the specified AP + * is not associated with a carrier network. + */ + public String getCarrierName(String ssid) { + NetworkInfo info = mCarrierNetworkMap.get(ssid); + return info == null ? null : info.mCarrierName; + } + + /** + * Utility class for storing carrier network information. + */ + private static class NetworkInfo { + final int mEapType; + final String mCarrierName; + + NetworkInfo(int eapType, String carrierName) { + mEapType = eapType; + mCarrierName = carrierName; + } + } + + /** + * Update the carrier network map based on the current carrier configuration of the active + * subscriptions. + * + * @param context Current application context + */ + private void updateNetworkConfig(Context context) { + // Reset network map. + mCarrierNetworkMap.clear(); + + CarrierConfigManager carrierConfigManager = + (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); + if (carrierConfigManager == null) { + return; + } + + SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE); + if (subscriptionManager == null) { + return; + } + List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList(); + if (subInfoList == null) { + return; + } + + // Process the carrier config for each active subscription. + for (SubscriptionInfo subInfo : subInfoList) { + processNetworkConfig( + carrierConfigManager.getConfigForSubId(subInfo.getSubscriptionId()), + subInfo.getDisplayName().toString()); + } + } + + /** + * Process the carrier network config, the network config string is formatted as follow: + * + * "[Base64 Encoded SSID],[EAP Type]" + * Where EAP Type is the standard EAP method number, refer to + * http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml for more info. + + * @param carrierConfig The bundle containing the carrier configuration + * @param carrierName The display name of the associated carrier + */ + private void processNetworkConfig(PersistableBundle carrierConfig, String carrierName) { + if (carrierConfig == null) { + return; + } + String[] networkConfigs = carrierConfig.getStringArray( + CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY); + if (networkConfigs == null) { + return; + } + + for (String networkConfig : networkConfigs) { + String[] configArr = networkConfig.split(NETWORK_CONFIG_SEPARATOR); + if (configArr.length != CONFIG_ELEMENT_SIZE) { + Log.e(TAG, "Ignore invalid config: " + networkConfig); + continue; + } + try { + String ssid = new String(Base64.decode( + configArr[ENCODED_SSID_INDEX], Base64.DEFAULT)); + int eapType = parseEapType(Integer.parseInt(configArr[EAP_TYPE_INDEX])); + // Verify EAP type, must be a SIM based EAP type. + if (eapType == -1) { + Log.e(TAG, "Invalid EAP type: " + configArr[EAP_TYPE_INDEX]); + continue; + } + mCarrierNetworkMap.put(ssid, new NetworkInfo(eapType, carrierName)); + } catch (NumberFormatException e) { + Log.e(TAG, "Failed to parse EAP type: " + e.getMessage()); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to decode SSID: " + e.getMessage()); + } + } + } + + /** + * Convert a standard SIM-based EAP type (SIM, AKA, AKA') to the internal EAP type as defined in + * {@link WifiEnterpriseConfig.Eap}. -1 will be returned if the given EAP type is not + * SIM-based. + * + * @return SIM-based EAP type as defined in {@link WifiEnterpriseConfig.Eap}, or -1 if not + * SIM-based EAP type + */ + private static int parseEapType(int eapType) { + if (eapType == EAPConstants.EAP_SIM) { + return WifiEnterpriseConfig.Eap.SIM; + } else if (eapType == EAPConstants.EAP_AKA) { + return WifiEnterpriseConfig.Eap.AKA; + } else if (eapType == EAPConstants.EAP_AKA_PRIME) { + return WifiEnterpriseConfig.Eap.AKA_PRIME; + } + return -1; + } +} diff --git a/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java new file mode 100644 index 000000000..38c0ad058 --- /dev/null +++ b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.wifi.ScanResult; + +import com.android.internal.R; +import com.android.internal.notification.SystemNotificationChannels; + +/** + * Helper to create notifications for {@link OpenNetworkNotifier}. + */ +public class ConnectToNetworkNotificationBuilder { + + /** Intent when user dismissed the "Connect to Network" notification. */ + public static final String ACTION_USER_DISMISSED_NOTIFICATION = + "com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION"; + + /** Intent when user tapped action button to connect to recommended network. */ + public static final String ACTION_CONNECT_TO_NETWORK = + "com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK"; + + /** Intent when user tapped action button to open Wi-Fi Settings. */ + public static final String ACTION_PICK_WIFI_NETWORK = + "com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK"; + + /** Intent when user tapped "Failed to connect" notification to open Wi-Fi Settings. */ + public static final String ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE = + "com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE"; + + private Context mContext; + private Resources mResources; + private FrameworkFacade mFrameworkFacade; + + public ConnectToNetworkNotificationBuilder( + Context context, + FrameworkFacade framework) { + mContext = context; + mResources = context.getResources(); + mFrameworkFacade = framework; + } + + /** + * Creates the connect to network notification that alerts users of a recommended connectable + * network. + * + * There are two actions - "Options" link to the Wi-Fi picker activity, and "Connect" prompts + * the connection to the recommended network. + * + * @param network The network to be recommended + */ + public Notification createConnectToNetworkNotification(ScanResult network) { + Notification.Action connectAction = new Notification.Action.Builder( + null /* icon */, + mResources.getText(R.string.wifi_available_action_connect), + getPrivateBroadcast(ACTION_CONNECT_TO_NETWORK)).build(); + Notification.Action allNetworksAction = new Notification.Action.Builder( + null /* icon */, + mResources.getText(R.string.wifi_available_action_all_networks), + getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK)).build(); + return createNotificationBuilder( + mContext.getText(R.string.wifi_available_title), network.SSID) + .addAction(connectAction) + .addAction(allNetworksAction) + .build(); + } + + /** + * Creates the notification that indicates the controller is attempting to connect to the + * recommended network. + * + * @param network The network to be recommended + */ + public Notification createNetworkConnectingNotification(ScanResult network) { + return createNotificationBuilder( + mContext.getText(R.string.wifi_available_title_connecting), network.SSID) + .setProgress(0 /* max */, 0 /* progress */, true /* indeterminate */) + .build(); + } + + /** + * Creates the notification that indicates the controller successfully connected to the + * recommended network. + * + * @param network The network to be recommended + */ + public Notification createNetworkConnectedNotification(ScanResult network) { + return createNotificationBuilder( + mContext.getText(R.string.wifi_available_title_connected), network.SSID) + .build(); + } + + /** + * Creates the notification that indicates the controller failed to connect to the recommended + * network. Tapping this notification opens the wifi picker. + */ + public Notification createNetworkFailedNotification() { + return createNotificationBuilder( + mContext.getText(R.string.wifi_available_title_failed_to_connect), + mContext.getText(R.string.wifi_available_content_failed_to_connect)) + .setContentIntent( + getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE)) + .setAutoCancel(true) + .build(); + } + + private Notification.Builder createNotificationBuilder( + CharSequence title, CharSequence content) { + return mFrameworkFacade.makeNotificationBuilder(mContext, + SystemNotificationChannels.NETWORK_AVAILABLE) + .setSmallIcon(R.drawable.stat_notify_wifi_in_range) + .setTicker(title) + .setContentTitle(title) + .setContentText(content) + .setDeleteIntent(getPrivateBroadcast(ACTION_USER_DISMISSED_NOTIFICATION)) + .setShowWhen(false) + .setLocalOnly(true) + .setColor(mResources.getColor(R.color.system_notification_accent_color, + mContext.getTheme())); + } + + private PendingIntent getPrivateBroadcast(String action) { + Intent intent = new Intent(action).setPackage("android"); + return mFrameworkFacade.getBroadcast( + mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } +} diff --git a/service/java/com/android/server/wifi/NetworkListStoreData.java b/service/java/com/android/server/wifi/NetworkListStoreData.java index 5ddfd4dfb..f287d4b98 100644 --- a/service/java/com/android/server/wifi/NetworkListStoreData.java +++ b/service/java/com/android/server/wifi/NetworkListStoreData.java @@ -16,10 +16,12 @@ package com.android.server.wifi; +import android.content.Context; import android.net.IpConfiguration; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.WifiEnterpriseConfig; +import android.os.Process; import android.util.Log; import android.util.Pair; @@ -52,6 +54,8 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData { private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION = "WifiEnterpriseConfiguration"; + private final Context mContext; + /** * List of saved shared networks visible to all the users to be stored in the shared store file. */ @@ -62,7 +66,9 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData { */ private List<WifiConfiguration> mUserConfigurations; - NetworkListStoreData() {} + NetworkListStoreData(Context context) { + mContext = context; + } @Override public void serializeData(XmlSerializer out, boolean shared) @@ -282,6 +288,19 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData { "Configuration key does not match. Retrieved: " + configKeyParsed + ", Calculated: " + configKeyCalculated); } + // Set creatorUid/creatorName for networks which don't have it set to valid value. + String creatorName = mContext.getPackageManager().getNameForUid(configuration.creatorUid); + if (creatorName == null) { + Log.e(TAG, "Invalid creatorUid for saved network " + configuration.configKey() + + ", creatorUid=" + configuration.creatorUid); + configuration.creatorUid = Process.SYSTEM_UID; + configuration.creatorName = creatorName; + } else if (!creatorName.equals(configuration.creatorName)) { + Log.w(TAG, "Invalid creatorName for saved network " + configuration.configKey() + + ", creatorUid=" + configuration.creatorUid + + ", creatorName=" + configuration.creatorName); + configuration.creatorName = creatorName; + } configuration.setNetworkSelectionStatus(status); configuration.setIpConfiguration(ipConfiguration); diff --git a/service/java/com/android/server/wifi/OpenNetworkNotifier.java b/service/java/com/android/server/wifi/OpenNetworkNotifier.java new file mode 100644 index 000000000..eee4ac53c --- /dev/null +++ b/service/java/com/android/server/wifi/OpenNetworkNotifier.java @@ -0,0 +1,498 @@ +/* + * 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 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 android.annotation.IntDef; +import android.annotation.NonNull; +import android.app.Notification; +import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount; +import com.android.server.wifi.util.ScanResultUtil; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Set; + +/** + * Takes care of handling the "open wi-fi network available" notification + * + * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread. + * @hide + */ +public class OpenNetworkNotifier { + + private static final String TAG = "OpenNetworkNotifier"; + + /** Time in milliseconds to display the Connecting notification. */ + private static final int TIME_TO_SHOW_CONNECTING_MILLIS = 10000; + + /** Time in milliseconds to display the Connected notification. */ + private static final int TIME_TO_SHOW_CONNECTED_MILLIS = 5000; + + /** Time in milliseconds to display the Failed To Connect notification. */ + private static final int TIME_TO_SHOW_FAILED_MILLIS = 5000; + + /** The state of the notification */ + @IntDef({ + STATE_NO_NOTIFICATION, + STATE_SHOWING_RECOMMENDATION_NOTIFICATION, + STATE_CONNECTING_IN_NOTIFICATION, + STATE_CONNECTED_NOTIFICATION, + STATE_CONNECT_FAILED_NOTIFICATION + }) + @Retention(RetentionPolicy.SOURCE) + private @interface State {} + + /** No recommendation is made and no notifications are shown. */ + private static final int STATE_NO_NOTIFICATION = 0; + /** The initial notification recommending an open network to connect to is shown. */ + private static final int STATE_SHOWING_RECOMMENDATION_NOTIFICATION = 1; + /** The notification of status of connecting to the recommended network is shown. */ + private static final int STATE_CONNECTING_IN_NOTIFICATION = 2; + /** The notification that the connection to the recommended network was successful is shown. */ + private static final int STATE_CONNECTED_NOTIFICATION = 3; + /** The notification to show that connection to the recommended network failed is shown. */ + private static final int STATE_CONNECT_FAILED_NOTIFICATION = 4; + + /** Current state of the notification. */ + @State private int mState = STATE_NO_NOTIFICATION; + + /** Identifier of the {@link SsidSetStoreData}. */ + private static final String STORE_DATA_IDENTIFIER = "OpenNetworkNotifierBlacklist"; + /** + * The {@link Clock#getWallClockMillis()} must be at least this value for us + * to show the notification again. + */ + private long mNotificationRepeatTime; + /** + * When a notification is shown, we wait this amount before possibly showing it again. + */ + private final long mNotificationRepeatDelay; + /** Default repeat delay in seconds. */ + @VisibleForTesting + static final int DEFAULT_REPEAT_DELAY_SEC = 900; + + /** Whether the user has set the setting to show the 'available networks' notification. */ + private boolean mSettingEnabled; + /** Whether the screen is on or not. */ + private boolean mScreenOn; + + /** List of SSIDs blacklisted from recommendation. */ + private final Set<String> mBlacklistedSsids; + + private final Context mContext; + private final Handler mHandler; + private final FrameworkFacade mFrameworkFacade; + private final WifiMetrics mWifiMetrics; + private final Clock mClock; + private final WifiConfigManager mConfigManager; + private final WifiStateMachine mWifiStateMachine; + private final Messenger mSrcMessenger; + private final OpenNetworkRecommender mOpenNetworkRecommender; + private final ConnectToNetworkNotificationBuilder mNotificationBuilder; + + private ScanResult mRecommendedNetwork; + + OpenNetworkNotifier( + Context context, + Looper looper, + FrameworkFacade framework, + Clock clock, + WifiMetrics wifiMetrics, + WifiConfigManager wifiConfigManager, + WifiConfigStore wifiConfigStore, + WifiStateMachine wifiStateMachine, + OpenNetworkRecommender openNetworkRecommender, + ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder) { + mContext = context; + mHandler = new Handler(looper); + mFrameworkFacade = framework; + mWifiMetrics = wifiMetrics; + mClock = clock; + mConfigManager = wifiConfigManager; + mWifiStateMachine = wifiStateMachine; + mOpenNetworkRecommender = openNetworkRecommender; + mNotificationBuilder = connectToNetworkNotificationBuilder; + mScreenOn = false; + mSrcMessenger = new Messenger(new Handler(looper, mConnectionStateCallback)); + + mBlacklistedSsids = new ArraySet<>(); + wifiConfigStore.registerStoreData(new SsidSetStoreData( + STORE_DATA_IDENTIFIER, new OpenNetworkNotifierStoreData())); + + // Setting is in seconds + mNotificationRepeatDelay = mFrameworkFacade.getIntegerSetting(context, + Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + DEFAULT_REPEAT_DELAY_SEC) * 1000L; + NotificationEnabledSettingObserver settingObserver = new NotificationEnabledSettingObserver( + mHandler); + settingObserver.register(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_USER_DISMISSED_NOTIFICATION); + filter.addAction(ACTION_CONNECT_TO_NETWORK); + filter.addAction(ACTION_PICK_WIFI_NETWORK); + filter.addAction(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE); + mContext.registerReceiver( + mBroadcastReceiver, filter, null /* broadcastPermission */, mHandler); + } + + private final BroadcastReceiver mBroadcastReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case ACTION_USER_DISMISSED_NOTIFICATION: + handleUserDismissedAction(); + break; + case ACTION_CONNECT_TO_NETWORK: + handleConnectToNetworkAction(); + break; + case ACTION_PICK_WIFI_NETWORK: + handleSeeAllNetworksAction(); + break; + case ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE: + handlePickWifiNetworkAfterConnectFailure(); + break; + default: + Log.e(TAG, "Unknown action " + intent.getAction()); + } + } + }; + + private final Handler.Callback mConnectionStateCallback = (Message msg) -> { + switch (msg.what) { + // 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(TAG, "Unknown message " + msg.what); + } + return true; + }; + + /** + * Clears the pending notification. This is called by {@link WifiConnectivityManager} on stop. + * + * @param resetRepeatTime resets the time delay for repeated notification if true. + */ + public void clearPendingNotification(boolean resetRepeatTime) { + if (resetRepeatTime) { + mNotificationRepeatTime = 0; + } + + if (mState != STATE_NO_NOTIFICATION) { + getNotificationManager().cancel(SystemMessage.NOTE_NETWORK_AVAILABLE); + + if (mRecommendedNetwork != null) { + Log.d(TAG, "Notification with state=" + + mState + + " was cleared for recommended network: " + + mRecommendedNetwork.SSID); + } + mState = STATE_NO_NOTIFICATION; + mRecommendedNetwork = null; + } + } + + private boolean isControllerEnabled() { + return mSettingEnabled && !UserManager.get(mContext) + .hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT); + } + + /** + * If there are open networks, attempt to post an open network notification. + * + * @param availableNetworks Available networks from + * {@link WifiNetworkSelector.NetworkEvaluator#getFilteredScanDetailsForOpenUnsavedNetworks()}. + */ + public void handleScanResults(@NonNull List<ScanDetail> availableNetworks) { + if (!isControllerEnabled()) { + clearPendingNotification(true /* resetRepeatTime */); + return; + } + if (availableNetworks.isEmpty()) { + clearPendingNotification(false /* resetRepeatTime */); + return; + } + + // Not enough time has passed to show a recommendation notification again + if (mState == STATE_NO_NOTIFICATION + && mClock.getWallClockMillis() < mNotificationRepeatTime) { + return; + } + + // Do nothing when the screen is off and no notification is showing. + if (mState == STATE_NO_NOTIFICATION && !mScreenOn) { + return; + } + + // Only show a new or update an existing recommendation notification. + if (mState == STATE_NO_NOTIFICATION + || mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { + ScanResult recommendation = mOpenNetworkRecommender.recommendNetwork( + availableNetworks, new ArraySet<>(mBlacklistedSsids)); + + if (recommendation != null) { + postInitialNotification(recommendation); + } else { + clearPendingNotification(false /* resetRepeatTime */); + } + } + } + + /** Handles screen state changes. */ + public void handleScreenStateChanged(boolean screenOn) { + mScreenOn = screenOn; + } + + /** + * Called by {@link WifiConnectivityManager} when Wi-Fi is connected. If the notification + * was in the connecting state, update the notification to show that it has connected to the + * recommended network. + */ + public void handleWifiConnected() { + if (mState != STATE_CONNECTING_IN_NOTIFICATION) { + clearPendingNotification(true /* resetRepeatTime */); + return; + } + + postNotification(mNotificationBuilder.createNetworkConnectedNotification( + mRecommendedNetwork)); + + Log.d(TAG, "User connected to recommended network: " + mRecommendedNetwork.SSID); + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK); + mState = STATE_CONNECTED_NOTIFICATION; + mHandler.postDelayed( + () -> { + if (mState == STATE_CONNECTED_NOTIFICATION) { + clearPendingNotification(true /* resetRepeatTime */); + } + }, + TIME_TO_SHOW_CONNECTED_MILLIS); + } + + /** + * Handles when a Wi-Fi connection attempt failed. + */ + public void handleConnectionFailure() { + if (mState != STATE_CONNECTING_IN_NOTIFICATION) { + return; + } + postNotification(mNotificationBuilder.createNetworkFailedNotification()); + + Log.d(TAG, "User failed to connect to recommended network: " + mRecommendedNetwork.SSID); + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT); + mState = STATE_CONNECT_FAILED_NOTIFICATION; + mHandler.postDelayed( + () -> { + if (mState == STATE_CONNECT_FAILED_NOTIFICATION) { + clearPendingNotification(false /* resetRepeatTime */); + } + }, + TIME_TO_SHOW_FAILED_MILLIS); + } + + private NotificationManager getNotificationManager() { + return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + } + + private void postInitialNotification(ScanResult recommendedNetwork) { + if (mRecommendedNetwork != null + && TextUtils.equals(mRecommendedNetwork.SSID, recommendedNetwork.SSID)) { + return; + } + postNotification(mNotificationBuilder.createConnectToNetworkNotification( + recommendedNetwork)); + if (mState == STATE_NO_NOTIFICATION) { + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + } else { + mWifiMetrics.incrementNumOpenNetworkRecommendationUpdates(); + } + mState = STATE_SHOWING_RECOMMENDATION_NOTIFICATION; + mRecommendedNetwork = recommendedNetwork; + mNotificationRepeatTime = mClock.getWallClockMillis() + mNotificationRepeatDelay; + } + + private void postNotification(Notification notification) { + getNotificationManager().notify(SystemMessage.NOTE_NETWORK_AVAILABLE, notification); + } + + private void handleConnectToNetworkAction() { + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); + if (mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { + return; + } + postNotification(mNotificationBuilder.createNetworkConnectingNotification( + mRecommendedNetwork)); + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); + + Log.d(TAG, "User initiated connection to recommended network: " + mRecommendedNetwork.SSID); + WifiConfiguration network = ScanResultUtil.createNetworkFromScanResult(mRecommendedNetwork); + Message msg = Message.obtain(); + msg.what = WifiManager.CONNECT_NETWORK; + msg.arg1 = WifiConfiguration.INVALID_NETWORK_ID; + msg.obj = network; + msg.replyTo = mSrcMessenger; + mWifiStateMachine.sendMessage(msg); + + mState = STATE_CONNECTING_IN_NOTIFICATION; + mHandler.postDelayed( + () -> { + if (mState == STATE_CONNECTING_IN_NOTIFICATION) { + handleConnectionFailure(); + } + }, + TIME_TO_SHOW_CONNECTING_MILLIS); + } + + private void handleSeeAllNetworksAction() { + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount.ACTION_PICK_WIFI_NETWORK); + startWifiSettings(); + } + + private void startWifiSettings() { + // Close notification drawer before opening the picker. + mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + mContext.startActivity( + new Intent(Settings.ACTION_WIFI_SETTINGS) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + clearPendingNotification(false /* resetRepeatTime */); + } + + private void handleConnectionAttemptFailedToSend() { + handleConnectionFailure(); + mWifiMetrics.incrementNumOpenNetworkConnectMessageFailedToSend(); + } + + private void handlePickWifiNetworkAfterConnectFailure() { + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount + .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE); + startWifiSettings(); + } + + private void handleUserDismissedAction() { + Log.d(TAG, "User dismissed notification with state=" + mState); + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount.ACTION_USER_DISMISSED_NOTIFICATION); + if (mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { + // blacklist dismissed network + mBlacklistedSsids.add(mRecommendedNetwork.SSID); + mWifiMetrics.setOpenNetworkRecommenderBlacklistSize(mBlacklistedSsids.size()); + mConfigManager.saveToStore(false /* forceWrite */); + Log.d(TAG, "Network is added to the open network notification blacklist: " + + mRecommendedNetwork.SSID); + } + resetStateAndDelayNotification(); + } + + private void resetStateAndDelayNotification() { + mState = STATE_NO_NOTIFICATION; + mNotificationRepeatTime = System.currentTimeMillis() + mNotificationRepeatDelay; + mRecommendedNetwork = null; + } + + /** Dump ONA controller state. */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("OpenNetworkNotifier: "); + pw.println("mSettingEnabled " + mSettingEnabled); + pw.println("currentTime: " + mClock.getWallClockMillis()); + pw.println("mNotificationRepeatTime: " + mNotificationRepeatTime); + pw.println("mState: " + mState); + pw.println("mBlacklistedSsids: " + mBlacklistedSsids.toString()); + } + + private class OpenNetworkNotifierStoreData implements SsidSetStoreData.DataSource { + @Override + public Set<String> getSsids() { + return new ArraySet<>(mBlacklistedSsids); + } + + @Override + public void setSsids(Set<String> ssidList) { + mBlacklistedSsids.addAll(ssidList); + mWifiMetrics.setOpenNetworkRecommenderBlacklistSize(mBlacklistedSsids.size()); + } + } + + private class NotificationEnabledSettingObserver extends ContentObserver { + NotificationEnabledSettingObserver(Handler handler) { + super(handler); + } + + public void register() { + mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor( + Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); + mSettingEnabled = getValue(); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + mSettingEnabled = getValue(); + clearPendingNotification(true /* resetRepeatTime */); + } + + private boolean getValue() { + boolean enabled = mFrameworkFacade.getIntegerSetting(mContext, + Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; + mWifiMetrics.setIsWifiNetworksAvailableNotificationEnabled(enabled); + return enabled; + } + } +} diff --git a/service/java/com/android/server/wifi/OpenNetworkRecommender.java b/service/java/com/android/server/wifi/OpenNetworkRecommender.java new file mode 100644 index 000000000..5ceeddded --- /dev/null +++ b/service/java/com/android/server/wifi/OpenNetworkRecommender.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.annotation.NonNull; +import android.net.wifi.ScanResult; + +import java.util.List; +import java.util.Set; + +/** + * Helps recommend the best available network for {@link OpenNetworkNotifier}. + * + * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread. + * @hide + */ +public class OpenNetworkRecommender { + + /** + * Recommends the network with the best signal strength. + * + * @param networks List of scan details to pick a recommendation. This list should not be null + * or empty. + * @param blacklistedSsids The list of SSIDs that should not be recommended. + */ + public ScanResult recommendNetwork(@NonNull List<ScanDetail> networks, + @NonNull Set<String> blacklistedSsids) { + ScanResult result = null; + int highestRssi = Integer.MIN_VALUE; + for (ScanDetail scanDetail : networks) { + ScanResult scanResult = scanDetail.getScanResult(); + + if (scanResult.level > highestRssi) { + result = scanResult; + highestRssi = scanResult.level; + } + } + + if (result != null && blacklistedSsids.contains(result.SSID)) { + result = null; + } + return result; + } +} diff --git a/service/java/com/android/server/wifi/RttService.java b/service/java/com/android/server/wifi/RttService.java index 7e4648b10..bd2736726 100644 --- a/service/java/com/android/server/wifi/RttService.java +++ b/service/java/com/android/server/wifi/RttService.java @@ -6,11 +6,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.net.wifi.IApInterface; -import android.net.wifi.IClientInterface; -import android.net.wifi.IInterfaceEventCallback; import android.net.wifi.IRttManager; -import android.net.wifi.IWificond; import android.net.wifi.RttManager; import android.net.wifi.RttManager.ResponderConfig; import android.net.wifi.WifiManager; @@ -26,6 +22,7 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.AsyncChannel; @@ -40,22 +37,64 @@ import java.io.PrintWriter; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; -import java.util.List; import java.util.Queue; import java.util.Set; public final class RttService extends SystemService { public static final boolean DBG = true; - private static final String WIFICOND_SERVICE_NAME = "wificond"; static class RttServiceImpl extends IRttManager.Stub { + private int mCurrentKey = 100; // increment on each usage + private final SparseArray<IBinder> mBinderByKey = new SparseArray<>(); @Override - public Messenger getMessenger() { + public Messenger getMessenger(IBinder binder, int[] key) { + if (key != null && key.length != 0) { + final int keyToUse = mCurrentKey++; + if (binder != null) { + try { + binder.linkToDeath(() -> { + // clean-up here if didn't get final registration + Slog.d(TAG, "Binder death on key=" + keyToUse); + mBinderByKey.delete(keyToUse); + }, 0); + mBinderByKey.put(keyToUse, binder); + } catch (RemoteException e) { + Slog.e(TAG, "getMessenger: can't link to death on binder: " + e); + return null; + } + } + + key[0] = keyToUse; + } return new Messenger(mClientHandler); } + private class RttDeathListener implements IBinder.DeathRecipient { + private final IBinder mBinder; + private final Messenger mReplyTo; + + RttDeathListener(IBinder binder, Messenger replyTo) { + mBinder = binder; + mReplyTo = replyTo; + } + + @Override + public void binderDied() { + if (DBG) Slog.d(TAG, "binder death for client mReplyTo=" + mReplyTo); + synchronized (mLock) { + ClientInfo ci = mClients.remove(mReplyTo); + if (ci != null) { + ci.cleanup(); + } else { + Slog.w(TAG, + "ClientInfo not found for terminated app -- mReplyTo=" + mReplyTo); + } + } + } + } + private class ClientHandler extends Handler { ClientHandler(android.os.Looper looper) { @@ -86,13 +125,30 @@ public final class RttService extends SystemService { case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: AsyncChannel ac = new AsyncChannel(); ac.connected(mContext, this, msg.replyTo); - ClientInfo client = new ClientInfo(ac, msg.sendingUid); + String packageName = msg.obj != null + ? ((RttManager.RttClient) msg.obj).getPackageName() : null; + ClientInfo client = new ClientInfo(ac, msg.sendingUid, packageName); synchronized (mLock) { mClients.put(msg.replyTo, client); } ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_SUCCESSFUL); return; + case RttManager.CMD_OP_REG_BINDER: { + int key = msg.arg1; + IBinder binder = mBinderByKey.get(key); + if (binder == null) { + Slog.e(TAG, "Can't find binder registered with key=" + key + " - no " + + "death listener!"); + return; + } + try { + binder.linkToDeath(new RttDeathListener(binder, msg.replyTo), 0); + } catch (RemoteException e) { + Slog.e(TAG, "Can't link to death for binder on key=" + key); + } + return; + } } ClientInfo ci; @@ -109,6 +165,12 @@ public final class RttService extends SystemService { "Client doesn't have LOCATION_HARDWARE permission"); return; } + if (!checkLocationPermission(ci)) { + replyFailed(msg, RttManager.REASON_PERMISSION_DENIED, + "Client doesn't have ACCESS_COARSE_LOCATION or " + + "ACCESS_FINE_LOCATION permission"); + return; + } final int validCommands[] = { RttManager.CMD_OP_START_RANGING, RttManager.CMD_OP_STOP_RANGING, @@ -141,9 +203,10 @@ public final class RttService extends SystemService { private final WifiNative mWifiNative; private final Context mContext; private final Looper mLooper; + private final WifiInjector mWifiInjector; + private RttStateMachine mStateMachine; private ClientHandler mClientHandler; - private WifiInjector mWifiInjector; RttServiceImpl(Context context, Looper looper, WifiInjector wifiInjector) { mContext = context; @@ -192,14 +255,16 @@ public final class RttService extends SystemService { private class ClientInfo { private final AsyncChannel mChannel; private final int mUid; + private final String mPackageName; ArrayMap<Integer, RttRequest> mRequests = new ArrayMap<>(); // Client keys of all outstanding responders. Set<Integer> mResponderRequests = new HashSet<>(); - ClientInfo(AsyncChannel channel, int uid) { + ClientInfo(AsyncChannel channel, int uid, String packageName) { mChannel = channel; mUid = uid; + mPackageName = packageName; } void addResponderRequest(int key) { @@ -293,37 +358,11 @@ public final class RttService extends SystemService { private static final int CMD_DRIVER_UNLOADED = BASE + 1; private static final int CMD_ISSUE_NEXT_REQUEST = BASE + 2; private static final int CMD_RTT_RESPONSE = BASE + 3; - private static final int CMD_CLIENT_INTERFACE_READY = BASE + 4; - private static final int CMD_CLIENT_INTERFACE_DOWN = BASE + 5; // Maximum duration for responder role. private static final int MAX_RESPONDER_DURATION_SECONDS = 60 * 10; - private static class InterfaceEventHandler extends IInterfaceEventCallback.Stub { - InterfaceEventHandler(RttStateMachine rttStateMachine) { - mRttStateMachine = rttStateMachine; - } - @Override - public void OnClientTorndownEvent(IClientInterface networkInterface) { - mRttStateMachine.sendMessage(CMD_CLIENT_INTERFACE_DOWN, networkInterface); - } - @Override - public void OnClientInterfaceReady(IClientInterface networkInterface) { - mRttStateMachine.sendMessage(CMD_CLIENT_INTERFACE_READY, networkInterface); - } - @Override - public void OnApTorndownEvent(IApInterface networkInterface) { } - @Override - public void OnApInterfaceReady(IApInterface networkInterface) { } - - private RttStateMachine mRttStateMachine; - } - class RttStateMachine extends StateMachine { - private IWificond mWificond; - private InterfaceEventHandler mInterfaceEventHandler; - private IClientInterface mClientInterface; - DefaultState mDefaultState = new DefaultState(); EnabledState mEnabledState = new EnabledState(); InitiatorEnabledState mInitiatorEnabledState = new InitiatorEnabledState(); @@ -385,39 +424,9 @@ public final class RttService extends SystemService { class EnabledState extends State { @Override public void enter() { - // This allows us to tolerate wificond restarts. - // When wificond restarts WifiStateMachine is supposed to go - // back to initial state and restart. - // 1) RttService watches for WIFI_STATE_ENABLED broadcasts - // 2) WifiStateMachine sends these broadcasts in the SupplicantStarted state - // 3) Since WSM will only be in SupplicantStarted for as long as wificond is - // alive, we refresh our wificond handler here and we don't subscribe to - // wificond's death explicitly. - mWificond = mWifiInjector.makeWificond(); - if (mWificond == null) { - Log.w(TAG, "Failed to get wificond binder handler"); - transitionTo(mDefaultState); - } - mInterfaceEventHandler = new InterfaceEventHandler(mStateMachine); - try { - mWificond.RegisterCallback(mInterfaceEventHandler); - // Get the current client interface, assuming there is at most - // one client interface for now. - List<IBinder> interfaces = mWificond.GetClientInterfaces(); - if (interfaces.size() > 0) { - mStateMachine.sendMessage( - CMD_CLIENT_INTERFACE_READY, - IClientInterface.Stub.asInterface(interfaces.get(0))); - } - } catch (RemoteException e1) { } - } @Override public void exit() { - try { - mWificond.UnregisterCallback(mInterfaceEventHandler); - } catch (RemoteException e1) { } - mInterfaceEventHandler = null; } @Override public boolean processMessage(Message msg) { @@ -481,14 +490,6 @@ public final class RttService extends SystemService { break; case RttManager.CMD_OP_DISABLE_RESPONDER: break; - case CMD_CLIENT_INTERFACE_DOWN: - if (mClientInterface == (IClientInterface) msg.obj) { - mClientInterface = null; - } - break; - case CMD_CLIENT_INTERFACE_READY: - mClientInterface = (IClientInterface) msg.obj; - break; default: return NOT_HANDLED; } @@ -534,8 +535,10 @@ public final class RttService extends SystemService { break; case CMD_RTT_RESPONSE: if (DBG) Log.d(TAG, "Received an RTT response from: " + msg.arg2); - mOutstandingRequest.ci.reportResult( - mOutstandingRequest, (RttManager.RttResult[])msg.obj); + if (checkLocationPermission(mOutstandingRequest.ci)) { + mOutstandingRequest.ci.reportResult( + mOutstandingRequest, (RttManager.RttResult[]) msg.obj); + } mOutstandingRequest = null; sendMessage(CMD_ISSUE_NEXT_REQUEST); break; @@ -659,7 +662,7 @@ public final class RttService extends SystemService { } } - boolean enforcePermissionCheck(Message msg) { + private boolean enforcePermissionCheck(Message msg) { try { mContext.enforcePermission(Manifest.permission.LOCATION_HARDWARE, -1, msg.sendingUid, "LocationRTT"); @@ -670,6 +673,12 @@ public final class RttService extends SystemService { return true; } + // Returns whether the client has location permission. + private boolean checkLocationPermission(ClientInfo clientInfo) { + return mWifiInjector.getWifiPermissionsUtil().checkCallersLocationPermission( + clientInfo.mPackageName, clientInfo.mUid); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -717,8 +726,11 @@ public final class RttService extends SystemService { if (DBG) Log.d(TAG, "No more requests left"); return null; } + @Override public RttManager.RttCapabilities getRttCapabilities() { + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to access rtt capabilities"); return mWifiNative.getRttCapabilities(); } } diff --git a/service/java/com/android/server/wifi/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java index 3b69a641b..abb6ad8b0 100644 --- a/service/java/com/android/server/wifi/ScanDetailCache.java +++ b/service/java/com/android/server/wifi/ScanDetailCache.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.os.SystemClock; @@ -73,7 +74,7 @@ public class ScanDetailCache { * @param bssid provided BSSID * @return {@code null} if no match ScanResult is found. */ - public ScanResult get(String bssid) { + public ScanResult getScanResult(String bssid) { ScanDetail scanDetail = getScanDetail(bssid); return scanDetail == null ? null : scanDetail.getScanResult(); } @@ -84,11 +85,11 @@ public class ScanDetailCache { * @param bssid provided BSSID * @return {@code null} if no match ScanDetail is found. */ - public ScanDetail getScanDetail(String bssid) { + public ScanDetail getScanDetail(@NonNull String bssid) { return mMap.get(bssid); } - void remove(String bssid) { + void remove(@NonNull String bssid) { mMap.remove(bssid); } diff --git a/service/java/com/android/server/wifi/SelfRecovery.java b/service/java/com/android/server/wifi/SelfRecovery.java index 21a3e0ac7..e39e0d5b0 100644 --- a/service/java/com/android/server/wifi/SelfRecovery.java +++ b/service/java/com/android/server/wifi/SelfRecovery.java @@ -72,7 +72,7 @@ public class SelfRecovery { Log.e(TAG, "Invalid trigger reason. Ignoring..."); return; } - Log.wtf(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]); + Log.e(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]); if (reason == REASON_WIFICOND_CRASH || reason == REASON_HAL_CRASH) { trimPastRestartTimes(); // Ensure there haven't been too many restarts within MAX_RESTARTS_TIME_WINDOW diff --git a/service/java/com/android/server/wifi/SsidSetStoreData.java b/service/java/com/android/server/wifi/SsidSetStoreData.java new file mode 100644 index 000000000..daed26a6a --- /dev/null +++ b/service/java/com/android/server/wifi/SsidSetStoreData.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.text.TextUtils; + +import com.android.server.wifi.util.XmlUtil; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * Store data for network notifiers. + * + * Below are the current configuration data for each respective store file: + * + * Share Store (system wide configurations) + * - No data + * + * User Store (user specific configurations) + * - Set of blacklisted SSIDs + */ +public class SsidSetStoreData implements WifiConfigStore.StoreData { + private static final String XML_TAG_SECTION_HEADER_SUFFIX = "ConfigData"; + private static final String XML_TAG_SSID_SET = "SSIDSet"; + + private final String mTagName; + private final DataSource mDataSource; + + /** + * Interface define the data source for the notifier store data. + */ + public interface DataSource { + /** + * Retrieve the SSID set from the data source. + * + * @return Set of SSIDs + */ + Set<String> getSsids(); + + /** + * Update the SSID set in the data source. + * + * @param ssidSet The set of SSIDs + */ + void setSsids(Set<String> ssidSet); + } + + /** + * Creates the SSID Set store data. + * + * @param name Identifier of the SSID set. + * @param dataSource The DataSource that implements the update and retrieval of the SSID set. + */ + SsidSetStoreData(String name, DataSource dataSource) { + mTagName = name + XML_TAG_SECTION_HEADER_SUFFIX; + mDataSource = dataSource; + } + + @Override + public void serializeData(XmlSerializer out, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + throw new XmlPullParserException("Share data not supported"); + } + Set<String> ssidSet = mDataSource.getSsids(); + if (ssidSet != null && !ssidSet.isEmpty()) { + XmlUtil.writeNextValue(out, XML_TAG_SSID_SET, mDataSource.getSsids()); + } + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + throw new XmlPullParserException("Share data not supported"); + } + + while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { + String[] valueName = new String[1]; + Object value = XmlUtil.readCurrentValue(in, valueName); + if (TextUtils.isEmpty(valueName[0])) { + throw new XmlPullParserException("Missing value name"); + } + switch (valueName[0]) { + case XML_TAG_SSID_SET: + mDataSource.setSsids((Set<String>) value); + break; + default: + throw new XmlPullParserException("Unknown tag under " + + mTagName + ": " + valueName[0]); + } + } + } + + @Override + public void resetData(boolean shared) { + if (!shared) { + mDataSource.setSsids(new HashSet<>()); + } + } + + @Override + public String getName() { + return mTagName; + } + + @Override + public boolean supportShareData() { + return false; + } +} diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java index 61ec9b3ab..b35989797 100644 --- a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java @@ -199,7 +199,7 @@ public class SupplicantStaNetworkHal { for (int i = 0; i < 4; i++) { config.wepKeys[i] = null; if (getWepKey(i) && !ArrayUtils.isEmpty(mWepKey)) { - config.wepKeys[i] = NativeUtil.bytesToHexOrQuotedAsciiString(mWepKey); + config.wepKeys[i] = NativeUtil.bytesToHexOrQuotedString(mWepKey); } } /** PSK pass phrase */ @@ -293,7 +293,7 @@ public class SupplicantStaNetworkHal { for (int i = 0; i < config.wepKeys.length; i++) { if (config.wepKeys[i] != null) { if (!setWepKey( - i, NativeUtil.hexOrQuotedAsciiStringToBytes(config.wepKeys[i]))) { + i, NativeUtil.hexOrQuotedStringToBytes(config.wepKeys[i]))) { Log.e(TAG, "failed to set wep_key " + i); return false; } diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 72c044f5b..f2563968a 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -47,7 +47,6 @@ import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; -import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy; import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.util.TelephonyUtil; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -248,7 +247,6 @@ public class WifiConfigManager { private final TelephonyManager mTelephonyManager; private final WifiKeyStore mWifiKeyStore; private final WifiConfigStore mWifiConfigStore; - private final WifiConfigStoreLegacy mWifiConfigStoreLegacy; private final WifiPermissionsUtil mWifiPermissionsUtil; private final WifiPermissionsWrapper mWifiPermissionsWrapper; /** @@ -342,7 +340,7 @@ public class WifiConfigManager { WifiConfigManager( Context context, Clock clock, UserManager userManager, TelephonyManager telephonyManager, WifiKeyStore wifiKeyStore, - WifiConfigStore wifiConfigStore, WifiConfigStoreLegacy wifiConfigStoreLegacy, + WifiConfigStore wifiConfigStore, WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper wifiPermissionsWrapper, NetworkListStoreData networkListStoreData, @@ -354,7 +352,6 @@ public class WifiConfigManager { mTelephonyManager = telephonyManager; mWifiKeyStore = wifiKeyStore; mWifiConfigStore = wifiConfigStore; - mWifiConfigStoreLegacy = wifiConfigStoreLegacy; mWifiPermissionsUtil = wifiPermissionsUtil; mWifiPermissionsWrapper = wifiPermissionsWrapper; @@ -652,6 +649,12 @@ public class WifiConfigManager { * @param ignoreLockdown Ignore the configuration lockdown checks for connection attempts. */ private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) { + // System internals can always update networks; they're typically only + // making meteredHint or meteredOverride changes + if (uid == Process.SYSTEM_UID) { + return true; + } + // Passpoint configurations are generated and managed by PasspointManager. They can be // added by either PasspointNetworkEvaluator (for auto connection) or Settings app // (for manual connection), and need to be removed once the connection is completed. @@ -711,16 +714,24 @@ public class WifiConfigManager { } /** - * Method to check if the provided UID belongs to the current foreground user or some other - * app (only SysUI today) running on behalf of the user. - * This is used to prevent any background user apps from modifying network configurations. + * Check if the given UID belongs to the current foreground user. This is + * used to prevent apps running in background users from modifying network + * configurations. + * <p> + * UIDs belonging to system internals (such as SystemUI) are always allowed, + * since they always run as {@link UserHandle#USER_SYSTEM}. * * @param uid uid of the app. - * @return true if the UID belongs to the current foreground app or SystemUI, false otherwise. + * @return true if the given UID belongs to the current foreground user, + * otherwise false. */ private boolean doesUidBelongToCurrentUser(int uid) { - return (WifiConfigurationUtil.doesUidBelongToAnyProfile( - uid, mUserManager.getProfiles(mCurrentUserId)) || (uid == mSystemUiUid)); + if (uid == android.os.Process.SYSTEM_UID || uid == mSystemUiUid) { + return true; + } else { + return WifiConfigurationUtil.doesUidBelongToAnyProfile( + uid, mUserManager.getProfiles(mCurrentUserId)); + } } /** @@ -829,6 +840,10 @@ public class WifiConfigManager { internalConfig.enterpriseConfig.copyFromExternal( externalConfig.enterpriseConfig, PASSWORD_MASK); } + + // Copy over any metered information. + internalConfig.meteredHint = externalConfig.meteredHint; + internalConfig.meteredOverride = externalConfig.meteredOverride; } /** @@ -891,7 +906,6 @@ public class WifiConfigManager { newInternalConfig.requirePMF = externalConfig.requirePMF; newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected; newInternalConfig.ephemeral = externalConfig.ephemeral; - newInternalConfig.meteredHint = externalConfig.meteredHint; newInternalConfig.useExternalScores = externalConfig.useExternalScores; newInternalConfig.shared = externalConfig.shared; @@ -1910,7 +1924,7 @@ public class WifiConfigManager { } // Adding a new BSSID - ScanResult result = scanDetailCache.get(scanResult.BSSID); + ScanResult result = scanDetailCache.getScanResult(scanResult.BSSID); if (result != null) { // transfer the black list status scanResult.blackListTimestamp = result.blackListTimestamp; @@ -2316,12 +2330,13 @@ public class WifiConfigManager { public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() { List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>(); List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks()); - // Remove any permanently disabled networks. + // Remove any permanently or temporarily disabled networks. Iterator<WifiConfiguration> iter = networks.iterator(); while (iter.hasNext()) { WifiConfiguration config = iter.next(); if (config.ephemeral || config.isPasspoint() - || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) { + || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() + || config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { iter.remove(); } } @@ -2557,10 +2572,12 @@ public class WifiConfigManager { * @param userId The identifier of the user that stopped. */ public void handleUserStop(int userId) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Handling user stop for " + userId); + } if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { saveToStore(true); - clearInternalData(); - mCurrentUserId = UserHandle.USER_SYSTEM; + clearInternalUserData(mCurrentUserId); } } @@ -2572,6 +2589,7 @@ public class WifiConfigManager { * - List of deleted ephemeral networks. */ private void clearInternalData() { + localLog("clearInternalData: Clearing all internal data"); mConfiguredNetworks.clear(); mDeletedEphemeralSSIDs.clear(); mScanDetailCaches.clear(); @@ -2590,12 +2608,16 @@ public class WifiConfigManager { * removed from memory. */ private Set<Integer> clearInternalUserData(int userId) { + localLog("clearInternalUserData: Clearing user internal data for " + userId); Set<Integer> removedNetworkIds = new HashSet<>(); // Remove any private networks of the old user before switching the userId. for (WifiConfiguration config : getInternalConfiguredNetworks()) { if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile( config.creatorUid, mUserManager.getProfiles(userId))) { removedNetworkIds.add(config.networkId); + localLog("clearInternalUserData: removed config." + + " netId=" + config.networkId + + " configKey=" + config.configKey()); mConfiguredNetworks.remove(config.networkId); } } @@ -2680,40 +2702,6 @@ public class WifiConfigManager { } /** - * Migrate data from legacy store files. The function performs the following operations: - * 1. Check if the legacy store files are present. - * 2. If yes, read all the data from the store files. - * 3. Save it to the new store files. - * 4. Delete the legacy store file. - * - * @return true if migration was successful or not needed (fresh install), false if it failed. - */ - public boolean migrateFromLegacyStore() { - if (!mWifiConfigStoreLegacy.areStoresPresent()) { - Log.d(TAG, "Legacy store files not found. No migration needed!"); - return true; - } - WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read(); - Log.d(TAG, "Reading from legacy store completed"); - loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(), - storeData.getDeletedEphemeralSSIDs()); - - // Setup user store for the current user in case it have not setup yet, so that data - // owned by the current user will be backed to the user store. - if (mDeferredUserUnlockRead) { - mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId)); - mDeferredUserUnlockRead = false; - } - - if (!saveToStore(true)) { - return false; - } - mWifiConfigStoreLegacy.removeStores(); - Log.d(TAG, "Migration from legacy store completed"); - return true; - } - - /** * Read the config store and load the in-memory lists from the store data retrieved and sends * out the networks changed broadcast. * @@ -2727,10 +2715,7 @@ public class WifiConfigManager { public boolean loadFromStore() { if (!mWifiConfigStore.areStoresPresent()) { Log.d(TAG, "New store files not found. No saved networks loaded!"); - if (!mWifiConfigStoreLegacy.areStoresPresent()) { - // No legacy store files either, so reset the pending store read flag. - mPendingStoreRead = false; - } + mPendingStoreRead = false; return true; } // If the user unlock comes in before we load from store, which means the user store have @@ -2908,4 +2893,29 @@ public class WifiConfigManager { public void setOnSavedNetworkUpdateListener(OnSavedNetworkUpdateListener listener) { mListener = listener; } + + /** + * Set extra failure reason for given config. Used to surface extra failure details to the UI + * @param netId The network ID of the config to set the extra failure reason for + * @param reason the WifiConfiguration.ExtraFailureReason failure code representing the most + * recent failure reason + */ + public void setRecentFailureAssociationStatus(int netId, int reason) { + WifiConfiguration config = getInternalConfiguredNetwork(netId); + if (config == null) { + return; + } + config.recentFailure.setAssociationStatus(reason); + } + + /** + * @param netId The network ID of the config to clear the extra failure reason from + */ + public void clearRecentFailureReason(int netId) { + WifiConfiguration config = getInternalConfiguredNetwork(netId); + if (config == null) { + return; + } + config.recentFailure.clear(); + } } diff --git a/service/java/com/android/server/wifi/WifiConfigStoreLegacy.java b/service/java/com/android/server/wifi/WifiConfigStoreLegacy.java deleted file mode 100644 index 867775511..000000000 --- a/service/java/com/android/server/wifi/WifiConfigStoreLegacy.java +++ /dev/null @@ -1,358 +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.net.IpConfiguration; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiEnterpriseConfig; -import android.os.Environment; -import android.util.Log; -import android.util.SparseArray; - -import com.android.server.net.IpConfigStore; -import com.android.server.wifi.hotspot2.LegacyPasspointConfig; -import com.android.server.wifi.hotspot2.LegacyPasspointConfigParser; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * This class provides the API's to load network configurations from legacy store - * mechanism (Pre O release). - * This class loads network configurations from: - * 1. /data/misc/wifi/networkHistory.txt - * 2. /data/misc/wifi/wpa_supplicant.conf - * 3. /data/misc/wifi/ipconfig.txt - * 4. /data/misc/wifi/PerProviderSubscription.conf - * - * The order of invocation of the public methods during migration is the following: - * 1. Check if legacy stores are present using {@link #areStoresPresent()}. - * 2. Load all the store data using {@link #read()} - * 3. Write the store data to the new store. - * 4. Remove all the legacy stores using {@link #removeStores()} - * - * NOTE: This class should only be used from WifiConfigManager and is not thread-safe! - * - * TODO(b/31065385): Passpoint config store data migration & deletion. - */ -public class WifiConfigStoreLegacy { - /** - * Log tag. - */ - private static final String TAG = "WifiConfigStoreLegacy"; - /** - * NetworkHistory config store file path. - */ - private static final File NETWORK_HISTORY_FILE = - new File(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE); - /** - * Passpoint config store file path. - */ - private static final File PPS_FILE = - new File(Environment.getDataMiscDirectory(), "wifi/PerProviderSubscription.conf"); - /** - * IpConfig config store file path. - */ - private static final File IP_CONFIG_FILE = - new File(Environment.getDataMiscDirectory(), "wifi/ipconfig.txt"); - /** - * List of external dependencies for WifiConfigManager. - */ - private final WifiNetworkHistory mWifiNetworkHistory; - private final WifiNative mWifiNative; - private final IpConfigStore mIpconfigStore; - - private final LegacyPasspointConfigParser mPasspointConfigParser; - - WifiConfigStoreLegacy(WifiNetworkHistory wifiNetworkHistory, - WifiNative wifiNative, IpConfigStore ipConfigStore, - LegacyPasspointConfigParser passpointConfigParser) { - mWifiNetworkHistory = wifiNetworkHistory; - mWifiNative = wifiNative; - mIpconfigStore = ipConfigStore; - mPasspointConfigParser = passpointConfigParser; - } - - /** - * Helper function to lookup the WifiConfiguration object from configKey to WifiConfiguration - * object map using the hashcode of the configKey. - * - * @param configurationMap Map of configKey to WifiConfiguration object. - * @param hashCode hash code of the configKey to match. - * @return - */ - private static WifiConfiguration lookupWifiConfigurationUsingConfigKeyHash( - Map<String, WifiConfiguration> configurationMap, int hashCode) { - for (Map.Entry<String, WifiConfiguration> entry : configurationMap.entrySet()) { - if (entry.getKey().hashCode() == hashCode) { - return entry.getValue(); - } - } - return null; - } - - /** - * Helper function to load {@link IpConfiguration} data from the ip config store file and - * populate the provided configuration map. - * - * @param configurationMap Map of configKey to WifiConfiguration object. - */ - private void loadFromIpConfigStore(Map<String, WifiConfiguration> configurationMap) { - // This is a map of the hash code of the network's configKey to the corresponding - // IpConfiguration. - SparseArray<IpConfiguration> ipConfigurations = - mIpconfigStore.readIpAndProxyConfigurations(IP_CONFIG_FILE.getAbsolutePath()); - if (ipConfigurations == null || ipConfigurations.size() == 0) { - Log.w(TAG, "No ip configurations found in ipconfig store"); - return; - } - for (int i = 0; i < ipConfigurations.size(); i++) { - int id = ipConfigurations.keyAt(i); - WifiConfiguration config = - lookupWifiConfigurationUsingConfigKeyHash(configurationMap, id); - // This is the only place the map is looked up through a (dangerous) hash-value! - if (config == null || config.ephemeral) { - Log.w(TAG, "configuration found for missing network, nid=" + id - + ", ignored, networks.size=" + Integer.toString(ipConfigurations.size())); - } else { - config.setIpConfiguration(ipConfigurations.valueAt(i)); - } - } - } - - /** - * Helper function to load {@link WifiConfiguration} data from networkHistory file and populate - * the provided configuration map and deleted ephemeral ssid list. - * - * @param configurationMap Map of configKey to WifiConfiguration object. - * @param deletedEphemeralSSIDs Map of configKey to WifiConfiguration object. - */ - private void loadFromNetworkHistory( - Map<String, WifiConfiguration> configurationMap, Set<String> deletedEphemeralSSIDs) { - // TODO: Need to revisit the scan detail cache persistance. We're not doing it in the new - // config store, so ignore it here as well. - Map<Integer, ScanDetailCache> scanDetailCaches = new HashMap<>(); - mWifiNetworkHistory.readNetworkHistory( - configurationMap, scanDetailCaches, deletedEphemeralSSIDs); - } - - /** - * Helper function to load {@link WifiConfiguration} data from wpa_supplicant and populate - * the provided configuration map and network extras. - * - * This method needs to manually parse the wpa_supplicant.conf file to retrieve some of the - * password fields like psk, wep_keys. password, etc. - * - * @param configurationMap Map of configKey to WifiConfiguration object. - * @param networkExtras Map of network extras parsed from wpa_supplicant. - */ - private void loadFromWpaSupplicant( - Map<String, WifiConfiguration> configurationMap, - SparseArray<Map<String, String>> networkExtras) { - if (!mWifiNative.migrateNetworksFromSupplicant(configurationMap, networkExtras)) { - Log.wtf(TAG, "Failed to load wifi configurations from wpa_supplicant"); - return; - } - if (configurationMap.isEmpty()) { - Log.w(TAG, "No wifi configurations found in wpa_supplicant"); - return; - } - } - - /** - * Helper function to update {@link WifiConfiguration} that represents a Passpoint - * configuration. - * - * This method will manually parse PerProviderSubscription.conf file to retrieve missing - * fields: provider friendly name, roaming consortium OIs, realm, IMSI. - * - * @param configurationMap Map of configKey to WifiConfiguration object. - * @param networkExtras Map of network extras parsed from wpa_supplicant. - */ - private void loadFromPasspointConfigStore( - Map<String, WifiConfiguration> configurationMap, - SparseArray<Map<String, String>> networkExtras) { - Map<String, LegacyPasspointConfig> passpointConfigMap = null; - try { - passpointConfigMap = mPasspointConfigParser.parseConfig(PPS_FILE.getAbsolutePath()); - } catch (IOException e) { - Log.w(TAG, "Failed to read/parse Passpoint config file: " + e.getMessage()); - } - - List<String> entriesToBeRemoved = new ArrayList<>(); - for (Map.Entry<String, WifiConfiguration> entry : configurationMap.entrySet()) { - WifiConfiguration wifiConfig = entry.getValue(); - // Ignore non-Enterprise network since enterprise configuration is required for - // Passpoint. - if (wifiConfig.enterpriseConfig == null || wifiConfig.enterpriseConfig.getEapMethod() - == WifiEnterpriseConfig.Eap.NONE) { - continue; - } - // Ignore configuration without FQDN. - Map<String, String> extras = networkExtras.get(wifiConfig.networkId); - if (extras == null || !extras.containsKey(SupplicantStaNetworkHal.ID_STRING_KEY_FQDN)) { - continue; - } - String fqdn = networkExtras.get(wifiConfig.networkId).get( - SupplicantStaNetworkHal.ID_STRING_KEY_FQDN); - - // Remove the configuration if failed to find the matching configuration in the - // Passpoint configuration file. - if (passpointConfigMap == null || !passpointConfigMap.containsKey(fqdn)) { - entriesToBeRemoved.add(entry.getKey()); - continue; - } - - // Update the missing Passpoint configuration fields to this WifiConfiguration. - LegacyPasspointConfig passpointConfig = passpointConfigMap.get(fqdn); - wifiConfig.isLegacyPasspointConfig = true; - wifiConfig.FQDN = fqdn; - wifiConfig.providerFriendlyName = passpointConfig.mFriendlyName; - if (passpointConfig.mRoamingConsortiumOis != null) { - wifiConfig.roamingConsortiumIds = Arrays.copyOf( - passpointConfig.mRoamingConsortiumOis, - passpointConfig.mRoamingConsortiumOis.length); - } - if (passpointConfig.mImsi != null) { - wifiConfig.enterpriseConfig.setPlmn(passpointConfig.mImsi); - } - if (passpointConfig.mRealm != null) { - wifiConfig.enterpriseConfig.setRealm(passpointConfig.mRealm); - } - } - - // Remove any incomplete Passpoint configurations. Should never happen, in case it does - // remove them to avoid maintaining any invalid Passpoint configurations. - for (String key : entriesToBeRemoved) { - Log.w(TAG, "Remove incomplete Passpoint configuration: " + key); - configurationMap.remove(key); - } - } - - /** - * Helper function to load from the different legacy stores: - * 1. Read the network configurations from wpa_supplicant using {@link WifiNative}. - * 2. Read the network configurations from networkHistory.txt using {@link WifiNetworkHistory}. - * 3. Read the Ip configurations from ipconfig.txt using {@link IpConfigStore}. - * 4. Read all the passpoint info from PerProviderSubscription.conf using - * {@link LegacyPasspointConfigParser}. - */ - public WifiConfigStoreDataLegacy read() { - final Map<String, WifiConfiguration> configurationMap = new HashMap<>(); - final SparseArray<Map<String, String>> networkExtras = new SparseArray<>(); - final Set<String> deletedEphemeralSSIDs = new HashSet<>(); - - loadFromWpaSupplicant(configurationMap, networkExtras); - loadFromNetworkHistory(configurationMap, deletedEphemeralSSIDs); - loadFromIpConfigStore(configurationMap); - loadFromPasspointConfigStore(configurationMap, networkExtras); - - // Now create config store data instance to be returned. - return new WifiConfigStoreDataLegacy( - new ArrayList<>(configurationMap.values()), deletedEphemeralSSIDs); - } - - /** - * Function to check if the legacy store files are present and hence load from those stores and - * then delete them. - * - * @return true if legacy store files are present, false otherwise. - */ - public boolean areStoresPresent() { - // We may have to keep the wpa_supplicant.conf file around. So, just use networkhistory.txt - // as a check to see if we have not yet migrated or not. This should be the last file - // that is deleted after migration. - File file = new File(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE); - return file.exists(); - } - - /** - * Method to remove all the legacy store files. This should only be invoked once all - * the data has been migrated to the new store file. - * 1. Removes all networks from wpa_supplicant and saves it to wpa_supplicant.conf - * 2. Deletes ipconfig.txt - * 3. Deletes networkHistory.txt - * - * @return true if all the store files were deleted successfully, false otherwise. - */ - public boolean removeStores() { - // TODO(b/29352330): Delete wpa_supplicant.conf file instead. - // First remove all networks from wpa_supplicant and save configuration. - if (!mWifiNative.removeAllNetworks()) { - Log.e(TAG, "Removing networks from wpa_supplicant failed"); - return false; - } - - // Now remove the ipconfig.txt file. - if (!IP_CONFIG_FILE.delete()) { - Log.e(TAG, "Removing ipconfig.txt failed"); - return false; - } - - // Now finally remove network history.txt - if (!NETWORK_HISTORY_FILE.delete()) { - Log.e(TAG, "Removing networkHistory.txt failed"); - return false; - } - - if (!PPS_FILE.delete()) { - Log.e(TAG, "Removing PerProviderSubscription.conf failed"); - return false; - } - - Log.i(TAG, "All legacy stores removed!"); - return true; - } - - /** - * Interface used to set a masked value in the provided configuration. The masked value is - * retrieved by parsing the wpa_supplicant.conf file. - */ - private interface MaskedWpaSupplicantFieldSetter { - void setValue(WifiConfiguration config, String value); - } - - /** - * Class used to encapsulate all the store data retrieved from the legacy (Pre O) store files. - */ - public static class WifiConfigStoreDataLegacy { - private List<WifiConfiguration> mConfigurations; - private Set<String> mDeletedEphemeralSSIDs; - // private List<HomeSP> mHomeSps; - - WifiConfigStoreDataLegacy(List<WifiConfiguration> configurations, - Set<String> deletedEphemeralSSIDs) { - mConfigurations = configurations; - mDeletedEphemeralSSIDs = deletedEphemeralSSIDs; - } - - public List<WifiConfiguration> getConfigurations() { - return mConfigurations; - } - - public Set<String> getDeletedEphemeralSSIDs() { - return mDeletedEphemeralSSIDs; - } - } -} diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java index fef78aade..dadc8a4e8 100644 --- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java +++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java @@ -345,7 +345,7 @@ public class WifiConfigurationUtil { } } try { - NativeUtil.hexOrQuotedAsciiStringToBytes(psk); + NativeUtil.hexOrQuotedStringToBytes(psk); } catch (IllegalArgumentException e) { Log.e(TAG, "validatePsk failed: malformed string: " + psk); return false; diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index 344242a14..458f73ae6 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -30,6 +30,8 @@ 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; import android.util.Log; @@ -133,7 +135,7 @@ public class WifiConnectivityManager { private final WifiConnectivityHelper mConnectivityHelper; private final WifiNetworkSelector mNetworkSelector; private final WifiLastResortWatchdog mWifiLastResortWatchdog; - private final WifiNotificationController mWifiNotificationController; + private final OpenNetworkNotifier mOpenNetworkNotifier; private final WifiMetrics mWifiMetrics; private final AlarmManager mAlarmManager; private final Handler mEventHandler; @@ -214,7 +216,7 @@ public class WifiConnectivityManager { @Override public void onAlarm() { - startSingleScan(mIsFullBandScan); + startSingleScan(mIsFullBandScan, WIFI_WORK_SOURCE); } } @@ -270,7 +272,7 @@ public class WifiConnectivityManager { return true; } else { if (mWifiState == WIFI_STATE_DISCONNECTED) { - mWifiNotificationController.handleScanResults( + mOpenNetworkNotifier.handleScanResults( mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks()); } return false; @@ -467,6 +469,10 @@ public class WifiConnectivityManager { @Override public void onPnoNetworkFound(ScanResult[] results) { for (ScanResult result: results) { + if (result.informationElements == null) { + localLog("Skipping scan result with null information elements"); + continue; + } mScanDetails.add(ScanResultUtil.toScanDetail(result)); } @@ -508,6 +514,8 @@ public class WifiConnectivityManager { } @Override public void onSavedNetworkUpdated(int networkId) { + // User might have changed meteredOverride, so update capabilties + mStateMachine.updateCapabilities(); updatePnoScan(); } @Override @@ -536,7 +544,7 @@ public class WifiConnectivityManager { WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo, WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, WifiLastResortWatchdog wifiLastResortWatchdog, - WifiNotificationController wifiNotificationController, WifiMetrics wifiMetrics, + OpenNetworkNotifier openNetworkNotifier, WifiMetrics wifiMetrics, Looper looper, Clock clock, LocalLog localLog, boolean enable, FrameworkFacade frameworkFacade, SavedNetworkEvaluator savedNetworkEvaluator, @@ -550,7 +558,7 @@ public class WifiConnectivityManager { mConnectivityHelper = connectivityHelper; mLocalLog = localLog; mWifiLastResortWatchdog = wifiLastResortWatchdog; - mWifiNotificationController = wifiNotificationController; + mOpenNetworkNotifier = openNetworkNotifier; mWifiMetrics = wifiMetrics; mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mEventHandler = new Handler(looper); @@ -558,9 +566,9 @@ public class WifiConnectivityManager { mConnectionAttemptTimeStamps = new LinkedList<>(); mMin5GHzRssi = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); + R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz); mMin24GHzRssi = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); + R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz); mBand5GHzBonus = context.getResources().getInteger( R.integer.config_wifi_framework_5GHz_preference_boost_factor); mCurrentConnectionBonus = context.getResources().getInteger( @@ -735,7 +743,7 @@ public class WifiConnectivityManager { localLog("connectToNetwork: Connect to " + targetAssociationId + " from " + currentAssociationId); } - mStateMachine.startConnectToNetwork(candidate.networkId, targetBssid); + mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid); } } @@ -788,7 +796,7 @@ public class WifiConnectivityManager { localLog("start a single scan from watchdogHandler"); scheduleWatchdogTimer(); - startSingleScan(true); + startSingleScan(true, WIFI_WORK_SOURCE); } } @@ -817,7 +825,7 @@ public class WifiConnectivityManager { } mLastPeriodicSingleScanTimeStamp = currentTimeStamp; - startSingleScan(isFullBandScan); + startSingleScan(isFullBandScan, WIFI_WORK_SOURCE); schedulePeriodicScanTimer(mPeriodicSingleScanInterval); // Set up the next scan interval in an exponential backoff fashion. @@ -844,7 +852,7 @@ public class WifiConnectivityManager { } // Start a single scan - private void startSingleScan(boolean isFullBandScan) { + private void startSingleScan(boolean isFullBandScan, WorkSource workSource) { if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { return; } @@ -869,7 +877,7 @@ public class WifiConnectivityManager { SingleScanListener singleScanListener = new SingleScanListener(isFullBandScan); - mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE); + mScanner.startScan(settings, singleScanListener, workSource); } // Start a periodic scan when screen is on @@ -1037,7 +1045,7 @@ public class WifiConnectivityManager { mScreenOn = screenOn; - mWifiNotificationController.handleScreenStateChanged(screenOn); + mOpenNetworkNotifier.handleScreenStateChanged(screenOn); startConnectivityScan(SCAN_ON_SCHEDULE); } @@ -1067,7 +1075,7 @@ public class WifiConnectivityManager { mWifiState = state; if (mWifiState == WIFI_STATE_CONNECTED) { - mWifiNotificationController.clearPendingNotification(false /* resetRepeatDelay */); + mOpenNetworkNotifier.handleWifiConnected(); } // Reset BSSID of last connection attempt and kick off @@ -1082,6 +1090,17 @@ public class WifiConnectivityManager { } /** + * Handler when a WiFi connection attempt ended. + * + * @param failureCode {@link WifiMetrics.ConnectionEvent} failure code. + */ + public void handleConnectionAttemptEnded(int failureCode) { + if (failureCode != WifiMetrics.ConnectionEvent.FAILURE_NONE) { + mOpenNetworkNotifier.handleConnectionFailure(); + } + } + + /** * Handler when user toggles whether untrusted connection is allowed */ public void setUntrustedConnectionAllowed(boolean allowed) { @@ -1115,11 +1134,11 @@ public class WifiConnectivityManager { /** * Handler for on-demand connectivity scan */ - public void forceConnectivityScan() { - localLog("forceConnectivityScan"); + public void forceConnectivityScan(WorkSource workSource) { + localLog("forceConnectivityScan in request of " + workSource); mWaitForFullBandScanResults = true; - startSingleScan(true); + startSingleScan(true, workSource); } /** @@ -1303,7 +1322,7 @@ public class WifiConnectivityManager { stopConnectivityScan(); clearBssidBlacklist(); resetLastPeriodicSingleScanTimeStamp(); - mWifiNotificationController.clearPendingNotification(true /* resetRepeatDelay */); + mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */); mLastConnectionAttemptBssid = null; mWaitForFullBandScanResults = false; } @@ -1363,6 +1382,6 @@ public class WifiConnectivityManager { pw.println("WifiConnectivityManager - Log Begin ----"); mLocalLog.dump(fd, pw, args); pw.println("WifiConnectivityManager - Log End ----"); - mWifiNotificationController.dump(fd, pw, args); + mOpenNetworkNotifier.dump(fd, pw, args); } } diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java index c1b186142..494ce86e5 100644 --- a/service/java/com/android/server/wifi/WifiController.java +++ b/service/java/com/android/server/wifi/WifiController.java @@ -248,9 +248,9 @@ public class WifiController extends StateMachine { } private void readWifiSleepPolicy() { - mSleepPolicy = mFacade.getIntegerSetting(mContext, - Settings.Global.WIFI_SLEEP_POLICY, - Settings.Global.WIFI_SLEEP_POLICY_NEVER); + // This should always set to default value because the settings menu to toggle this + // has been removed now. + mSleepPolicy = Settings.Global.WIFI_SLEEP_POLICY_NEVER; } private void readWifiReEnableDelay() { diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java index e69fb8e1c..66a035f08 100644 --- a/service/java/com/android/server/wifi/WifiCountryCode.java +++ b/service/java/com/android/server/wifi/WifiCountryCode.java @@ -83,11 +83,9 @@ public class WifiCountryCode { public synchronized void simCardRemoved() { if (DBG) Log.d(TAG, "SIM Card Removed"); // SIM card is removed, we need to reset the country code to phone default. - if (mRevertCountryCodeOnCellularLoss) { - mTelephonyCountryCode = null; - if (mReady) { - updateCountryCode(); - } + mTelephonyCountryCode = null; + if (mReady) { + updateCountryCode(); } } @@ -98,12 +96,9 @@ public class WifiCountryCode { */ public synchronized void airplaneModeEnabled() { if (DBG) Log.d(TAG, "Airplane Mode Enabled"); - mTelephonyCountryCode = null; // Airplane mode is enabled, we need to reset the country code to phone default. - if (mRevertCountryCodeOnCellularLoss) { - mTelephonyCountryCode = null; - // Country code will be set upon when wpa_supplicant starts next time. - } + // Country code will be set upon when wpa_supplicant starts next time. + mTelephonyCountryCode = null; } /** @@ -133,8 +128,10 @@ public class WifiCountryCode { if (DBG) Log.d(TAG, "Receive set country code request: " + countryCode); // Empty country code. if (TextUtils.isEmpty(countryCode)) { - if (DBG) Log.d(TAG, "Received empty country code, reset to default country code"); - mTelephonyCountryCode = null; + if (mRevertCountryCodeOnCellularLoss) { + if (DBG) Log.d(TAG, "Received empty country code, reset to default country code"); + mTelephonyCountryCode = null; + } } else { mTelephonyCountryCode = countryCode.toUpperCase(); } diff --git a/service/java/com/android/server/wifi/WifiDiagnostics.java b/service/java/com/android/server/wifi/WifiDiagnostics.java index 42078ef58..921faa306 100644 --- a/service/java/com/android/server/wifi/WifiDiagnostics.java +++ b/service/java/com/android/server/wifi/WifiDiagnostics.java @@ -77,6 +77,8 @@ class WifiDiagnostics extends BaseWifiDiagnostics { public static final int REPORT_REASON_UNEXPECTED_DISCONNECT = 5; public static final int REPORT_REASON_SCAN_FAILURE = 6; public static final int REPORT_REASON_USER_ACTION = 7; + public static final int REPORT_REASON_WIFICOND_CRASH = 8; + public static final int REPORT_REASON_HAL_CRASH = 9; /** number of bug reports to hold */ public static final int MAX_BUG_REPORTS = 4; diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 80daaeaa1..4b8e6829e 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -45,7 +45,6 @@ import com.android.server.am.BatteryStatsService; import com.android.server.net.DelayedDiskWrite; import com.android.server.net.IpConfigStore; import com.android.server.wifi.aware.WifiAwareMetrics; -import com.android.server.wifi.hotspot2.LegacyPasspointConfigParser; import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator; import com.android.server.wifi.hotspot2.PasspointObjectFactory; @@ -87,7 +86,7 @@ public class WifiInjector { private final WifiStateMachine mWifiStateMachine; private final WifiSettingsStore mSettingsStore; private final WifiCertManager mCertManager; - private final WifiNotificationController mNotificationController; + private final OpenNetworkNotifier mOpenNetworkNotifier; private final WifiLockManager mLockManager; private final WifiController mWifiController; private final WificondControl mWificondControl; @@ -101,9 +100,7 @@ public class WifiInjector { private final WifiMulticastLockManager mWifiMulticastLockManager; private final WifiConfigStore mWifiConfigStore; private final WifiKeyStore mWifiKeyStore; - private final WifiNetworkHistory mWifiNetworkHistory; private final IpConfigStore mIpConfigStore; - private final WifiConfigStoreLegacy mWifiConfigStoreLegacy; private final WifiConfigManager mWifiConfigManager; private final WifiConnectivityHelper mWifiConnectivityHelper; private final LocalLog mConnectivityLocalLog; @@ -169,7 +166,8 @@ public class WifiInjector { mWifiVendorHal = new WifiVendorHal(mHalDeviceManager, mWifiStateMachineHandlerThread.getLooper()); mSupplicantStaIfaceHal = new SupplicantStaIfaceHal(mContext, mWifiMonitor); - mWificondControl = new WificondControl(this, mWifiMonitor); + mWificondControl = new WificondControl(this, mWifiMonitor, + new CarrierNetworkConfig(mContext)); mWifiNative = new WifiNative(SystemProperties.get("wifi.interface", "wlan0"), mWifiVendorHal, mSupplicantStaIfaceHal, mWificondControl); mWifiP2pMonitor = new WifiP2pMonitor(this); @@ -194,16 +192,12 @@ public class WifiInjector { WifiConfigStore.createSharedFile()); // Legacy config store DelayedDiskWrite writer = new DelayedDiskWrite(); - mWifiNetworkHistory = new WifiNetworkHistory(mContext, writer); mIpConfigStore = new IpConfigStore(writer); - mWifiConfigStoreLegacy = new WifiConfigStoreLegacy( - mWifiNetworkHistory, mWifiNative, mIpConfigStore, - new LegacyPasspointConfigParser()); // Config Manager mWifiConfigManager = new WifiConfigManager(mContext, mClock, UserManager.get(mContext), TelephonyManager.from(mContext), - mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, mWifiPermissionsUtil, - mWifiPermissionsWrapper, new NetworkListStoreData(), + mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil, + mWifiPermissionsWrapper, new NetworkListStoreData(mContext), new DeletedEphemeralSsidsStoreData()); mWifiMetrics.setWifiConfigManager(mWifiConfigManager); mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative); @@ -230,8 +224,11 @@ public class WifiInjector { this, mBackupManagerProxy, mCountryCode, mWifiNative, new WrongPasswordNotifier(mContext, mFrameworkFacade)); mCertManager = new WifiCertManager(mContext); - mNotificationController = new WifiNotificationController(mContext, - mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, null); + mOpenNetworkNotifier = new OpenNetworkNotifier(mContext, + mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, + mWifiConfigManager, mWifiConfigStore, mWifiStateMachine, + new OpenNetworkRecommender(), + new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade)); mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService()); mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore, mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade); @@ -430,7 +427,7 @@ public class WifiInjector { boolean hasConnectionRequests) { return new WifiConnectivityManager(mContext, mWifiStateMachine, getWifiScanner(), mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper, - mWifiLastResortWatchdog, mNotificationController, mWifiMetrics, + mWifiLastResortWatchdog, mOpenNetworkNotifier, mWifiMetrics, mWifiStateMachineHandlerThread.getLooper(), mClock, mConnectivityLocalLog, hasConnectionRequests, mFrameworkFacade, mSavedNetworkEvaluator, mScoredNetworkEvaluator, mPasspointNetworkEvaluator); diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 7dfdb86d3..071b4f883 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -37,6 +37,8 @@ import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.hotspot2.PasspointMatch; import com.android.server.wifi.hotspot2.PasspointProvider; import com.android.server.wifi.nano.WifiMetricsProto; +import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount; +import com.android.server.wifi.nano.WifiMetricsProto.PnoScanMetrics; import com.android.server.wifi.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo; import com.android.server.wifi.util.InformationElementUtil; @@ -82,10 +84,12 @@ public class WifiMetrics { public static final int MAX_CONNECTABLE_BSSID_NETWORK_BUCKET = 50; public static final int MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET = 100; public static final int MAX_TOTAL_SCAN_RESULTS_BUCKET = 250; + private static final int CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER = 1000; private Clock mClock; private boolean mScreenOn; private int mWifiState; private WifiAwareMetrics mWifiAwareMetrics; + private final PnoScanMetrics mPnoScanMetrics = new PnoScanMetrics(); private Handler mHandler; private WifiConfigManager mWifiConfigManager; private WifiNetworkSelector mWifiNetworkSelector; @@ -148,6 +152,15 @@ public class WifiMetrics { private final SparseIntArray mAvailableSavedPasspointProviderBssidsInScanHistogram = new SparseIntArray(); + /** Mapping of "Connect to Network" notifications to counts. */ + private final SparseIntArray mConnectToNetworkNotificationCount = new SparseIntArray(); + /** Mapping of "Connect to Network" notification user actions to counts. */ + private final SparseIntArray mConnectToNetworkNotificationActionCount = new SparseIntArray(); + private int mOpenNetworkRecommenderBlacklistSize = 0; + private boolean mIsWifiNetworksAvailableNotificationOn = false; + private int mNumOpenNetworkConnectMessageFailedToSend = 0; + private int mNumOpenNetworkRecommendationUpdates = 0; + class RouterFingerPrint { private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto; RouterFingerPrint() { @@ -407,6 +420,51 @@ public class WifiMetrics { mPasspointManager = passpointManager; } + /** + * Increment total number of attempts to start a pno scan + */ + public void incrementPnoScanStartAttempCount() { + synchronized (mLock) { + mPnoScanMetrics.numPnoScanAttempts++; + } + } + + /** + * Increment total number of attempts with pno scan failed + */ + public void incrementPnoScanFailedCount() { + synchronized (mLock) { + mPnoScanMetrics.numPnoScanFailed++; + } + } + + /** + * 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() { + synchronized (mLock) { + mPnoScanMetrics.numPnoFoundNetworkEvents++; + } + } + // Values used for indexing SystemStateEntries private static final int SCREEN_ON = 1; private static final int SCREEN_OFF = 0; @@ -1190,6 +1248,55 @@ public class WifiMetrics { } } + /** Increments the occurence of a "Connect to Network" notification. */ + public void incrementConnectToNetworkNotification(int notificationType) { + synchronized (mLock) { + int count = mConnectToNetworkNotificationCount.get(notificationType); + mConnectToNetworkNotificationCount.put(notificationType, count + 1); + } + } + + /** Increments the occurence of an "Connect to Network" notification user action. */ + public void incrementConnectToNetworkNotificationAction(int notificationType, int actionType) { + synchronized (mLock) { + int key = notificationType * CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER + + actionType; + int count = mConnectToNetworkNotificationActionCount.get(key); + mConnectToNetworkNotificationActionCount.put(key, count + 1); + } + } + + /** + * Sets the number of SSIDs blacklisted from recommendation by the open network notification + * recommender. + */ + public void setOpenNetworkRecommenderBlacklistSize(int size) { + synchronized (mLock) { + mOpenNetworkRecommenderBlacklistSize = size; + } + } + + /** Sets if the available network notification feature is enabled. */ + public void setIsWifiNetworksAvailableNotificationEnabled(boolean enabled) { + synchronized (mLock) { + mIsWifiNetworksAvailableNotificationOn = enabled; + } + } + + /** Increments the occurence of connection attempts that were initiated unsuccessfully */ + public void incrementNumOpenNetworkRecommendationUpdates() { + synchronized (mLock) { + mNumOpenNetworkRecommendationUpdates++; + } + } + + /** Increments the occurence of connection attempts that were initiated unsuccessfully */ + public void incrementNumOpenNetworkConnectMessageFailedToSend() { + synchronized (mLock) { + mNumOpenNetworkConnectMessageFailedToSend++; + } + } + public static final String PROTO_DUMP_ARG = "wifiMetricsProto"; public static final String CLEAN_DUMP_ARG = "clean"; @@ -1388,8 +1495,8 @@ public class WifiMetrics { pw.println("mWifiLogProto.numWifiOnFailureDueToWificond=" + mWifiLogProto.numWifiOnFailureDueToWificond); pw.println("StaEventList:"); - for (StaEvent event : mStaEventList) { - pw.println(staEventToString(event)); + for (StaEventWithTime event : mStaEventList) { + pw.println(event); } pw.println("mWifiLogProto.numPasspointProviders=" @@ -1430,6 +1537,30 @@ public class WifiMetrics { + mWifiLogProto.fullBandAllSingleScanListenerResults); pw.println("mWifiAwareMetrics:"); mWifiAwareMetrics.dump(fd, pw, args); + + pw.println("mPnoScanMetrics.numPnoScanAttempts=" + + mPnoScanMetrics.numPnoScanAttempts); + pw.println("mPnoScanMetrics.numPnoScanFailed=" + + mPnoScanMetrics.numPnoScanFailed); + pw.println("mPnoScanMetrics.numPnoScanStartedOverOffload=" + + mPnoScanMetrics.numPnoScanStartedOverOffload); + pw.println("mPnoScanMetrics.numPnoScanFailedOverOffload=" + + mPnoScanMetrics.numPnoScanFailedOverOffload); + pw.println("mPnoScanMetrics.numPnoFoundNetworkEvents=" + + mPnoScanMetrics.numPnoFoundNetworkEvents); + + pw.println("mWifiLogProto.connectToNetworkNotificationCount=" + + mConnectToNetworkNotificationCount.toString()); + pw.println("mWifiLogProto.connectToNetworkNotificationActionCount=" + + mConnectToNetworkNotificationActionCount.toString()); + pw.println("mWifiLogProto.openNetworkRecommenderBlacklistSize=" + + mOpenNetworkRecommenderBlacklistSize); + pw.println("mWifiLogProto.isWifiNetworksAvailableNotificationOn=" + + mIsWifiNetworksAvailableNotificationOn); + pw.println("mWifiLogProto.numOpenNetworkRecommendationUpdates=" + + mNumOpenNetworkRecommendationUpdates); + pw.println("mWifiLogProto.numOpenNetworkConnectMessageFailedToSend=" + + mNumOpenNetworkConnectMessageFailedToSend); } } } @@ -1605,6 +1736,14 @@ public class WifiMetrics { mWifiLogProto.softApReturnCode[sapCode].count = mSoftApManagerReturnCodeCounts.valueAt(sapCode); } + + /** + * Convert StaEventList to array of StaEvents + */ + mWifiLogProto.staEventList = new StaEvent[mStaEventList.size()]; + for (int i = 0; i < mStaEventList.size(); i++) { + mWifiLogProto.staEventList[i] = mStaEventList.get(i).staEvent; + } mWifiLogProto.totalSsidsInScanHistogram = makeNumConnectableNetworksBucketArray(mTotalSsidsInScanHistogram); mWifiLogProto.totalBssidsInScanHistogram = @@ -1629,8 +1768,56 @@ public class WifiMetrics { mWifiLogProto.availableSavedPasspointProviderBssidsInScanHistogram = makeNumConnectableNetworksBucketArray( mAvailableSavedPasspointProviderBssidsInScanHistogram); - mWifiLogProto.staEventList = mStaEventList.toArray(mWifiLogProto.staEventList); mWifiLogProto.wifiAwareLog = mWifiAwareMetrics.consolidateProto(); + + mWifiLogProto.pnoScanMetrics = mPnoScanMetrics; + + /** + * Convert the SparseIntArray of "Connect to Network" notification types and counts to + * proto's repeated IntKeyVal array. + */ + ConnectToNetworkNotificationAndActionCount[] notificationCountArray = + new ConnectToNetworkNotificationAndActionCount[ + mConnectToNetworkNotificationCount.size()]; + for (int i = 0; i < mConnectToNetworkNotificationCount.size(); i++) { + ConnectToNetworkNotificationAndActionCount keyVal = + new ConnectToNetworkNotificationAndActionCount(); + keyVal.notification = mConnectToNetworkNotificationCount.keyAt(i); + keyVal.recommender = + ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN; + keyVal.count = mConnectToNetworkNotificationCount.valueAt(i); + notificationCountArray[i] = keyVal; + } + mWifiLogProto.connectToNetworkNotificationCount = notificationCountArray; + + /** + * Convert the SparseIntArray of "Connect to Network" notification types and counts to + * proto's repeated IntKeyVal array. + */ + ConnectToNetworkNotificationAndActionCount[] notificationActionCountArray = + new ConnectToNetworkNotificationAndActionCount[ + mConnectToNetworkNotificationActionCount.size()]; + for (int i = 0; i < mConnectToNetworkNotificationActionCount.size(); i++) { + ConnectToNetworkNotificationAndActionCount keyVal = + new ConnectToNetworkNotificationAndActionCount(); + int key = mConnectToNetworkNotificationActionCount.keyAt(i); + keyVal.notification = key / CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER; + keyVal.action = key % CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER; + keyVal.recommender = + ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN; + keyVal.count = mConnectToNetworkNotificationActionCount.valueAt(i); + notificationActionCountArray[i] = keyVal; + } + mWifiLogProto.connectToNetworkNotificationActionCount = notificationActionCountArray; + + mWifiLogProto.openNetworkRecommenderBlacklistSize = + mOpenNetworkRecommenderBlacklistSize; + mWifiLogProto.isWifiNetworksAvailableNotificationOn = + mIsWifiNetworksAvailableNotificationOn; + mWifiLogProto.numOpenNetworkRecommendationUpdates = + mNumOpenNetworkRecommendationUpdates; + mWifiLogProto.numOpenNetworkConnectMessageFailedToSend = + mNumOpenNetworkConnectMessageFailedToSend; } } @@ -1649,7 +1836,8 @@ public class WifiMetrics { } /** - * Clear all WifiMetrics, except for currentConnectionEvent. + * Clear all WifiMetrics, except for currentConnectionEvent and Open Network Notification + * feature enabled state, blacklist size. */ private void clear() { synchronized (mLock) { @@ -1679,6 +1867,11 @@ public class WifiMetrics { mAvailableOpenOrSavedBssidsInScanHistogram.clear(); mAvailableSavedPasspointProviderProfilesInScanHistogram.clear(); mAvailableSavedPasspointProviderBssidsInScanHistogram.clear(); + mPnoScanMetrics.clear(); + mConnectToNetworkNotificationCount.clear(); + mConnectToNetworkNotificationActionCount.clear(); + mNumOpenNetworkRecommendationUpdates = 0; + mNumOpenNetworkConnectMessageFailedToSend = 0; } } @@ -1826,7 +2019,7 @@ public class WifiMetrics { mLastPollRssi = -127; mLastPollFreq = -1; mLastPollLinkSpeed = -1; - mStaEventList.add(staEvent); + mStaEventList.add(new StaEventWithTime(staEvent, mClock.getWallClockMillis())); // Prune StaEventList if it gets too long if (mStaEventList.size() > MAX_STA_EVENTS) mStaEventList.remove(); } @@ -1903,7 +2096,7 @@ public class WifiMetrics { private static String supplicantStateChangesBitmaskToString(int mask) { StringBuilder sb = new StringBuilder(); - sb.append("SUPPLICANT_STATE_CHANGE_EVENTS: {"); + sb.append("supplicantStateChangeEvents: {"); if ((mask & (1 << StaEvent.STATE_DISCONNECTED)) > 0) sb.append(" DISCONNECTED"); if ((mask & (1 << StaEvent.STATE_INTERFACE_DISABLED)) > 0) sb.append(" INTERFACE_DISABLED"); if ((mask & (1 << StaEvent.STATE_INACTIVE)) > 0) sb.append(" INACTIVE"); @@ -1928,58 +2121,56 @@ public class WifiMetrics { public static String staEventToString(StaEvent event) { if (event == null) return "<NULL>"; StringBuilder sb = new StringBuilder(); - Long time = event.startTimeMillis; - sb.append(String.format("%9d ", time.longValue())).append(" "); switch (event.type) { case StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT: - sb.append("ASSOCIATION_REJECTION_EVENT:") + sb.append("ASSOCIATION_REJECTION_EVENT") .append(" timedOut=").append(event.associationTimedOut) .append(" status=").append(event.status).append(":") .append(ISupplicantStaIfaceCallback.StatusCode.toString(event.status)); break; case StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT: - sb.append("AUTHENTICATION_FAILURE_EVENT: reason=").append(event.authFailureReason) + sb.append("AUTHENTICATION_FAILURE_EVENT reason=").append(event.authFailureReason) .append(":").append(authFailureReasonToString(event.authFailureReason)); break; case StaEvent.TYPE_NETWORK_CONNECTION_EVENT: - sb.append("NETWORK_CONNECTION_EVENT:"); + sb.append("NETWORK_CONNECTION_EVENT"); break; case StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT: - sb.append("NETWORK_DISCONNECTION_EVENT:") + sb.append("NETWORK_DISCONNECTION_EVENT") .append(" local_gen=").append(event.localGen) .append(" reason=").append(event.reason).append(":") .append(ISupplicantStaIfaceCallback.ReasonCode.toString( (event.reason >= 0 ? event.reason : -1 * event.reason))); break; case StaEvent.TYPE_CMD_ASSOCIATED_BSSID: - sb.append("CMD_ASSOCIATED_BSSID:"); + sb.append("CMD_ASSOCIATED_BSSID"); break; case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL: - sb.append("CMD_IP_CONFIGURATION_SUCCESSFUL:"); + sb.append("CMD_IP_CONFIGURATION_SUCCESSFUL"); break; case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST: - sb.append("CMD_IP_CONFIGURATION_LOST:"); + sb.append("CMD_IP_CONFIGURATION_LOST"); break; case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST: - sb.append("CMD_IP_REACHABILITY_LOST:"); + sb.append("CMD_IP_REACHABILITY_LOST"); break; case StaEvent.TYPE_CMD_TARGET_BSSID: - sb.append("CMD_TARGET_BSSID:"); + sb.append("CMD_TARGET_BSSID"); break; case StaEvent.TYPE_CMD_START_CONNECT: - sb.append("CMD_START_CONNECT:"); + sb.append("CMD_START_CONNECT"); break; case StaEvent.TYPE_CMD_START_ROAM: - sb.append("CMD_START_ROAM:"); + sb.append("CMD_START_ROAM"); break; case StaEvent.TYPE_CONNECT_NETWORK: - sb.append("CONNECT_NETWORK:"); + sb.append("CONNECT_NETWORK"); break; case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK: - sb.append("NETWORK_AGENT_VALID_NETWORK:"); + sb.append("NETWORK_AGENT_VALID_NETWORK"); break; case StaEvent.TYPE_FRAMEWORK_DISCONNECT: - sb.append("FRAMEWORK_DISCONNECT:") + sb.append("FRAMEWORK_DISCONNECT") .append(" reason=") .append(frameworkDisconnectReasonToString(event.frameworkDisconnectReason)); break; @@ -1991,11 +2182,11 @@ public class WifiMetrics { if (event.lastFreq != -1) sb.append(" lastFreq=").append(event.lastFreq); if (event.lastLinkSpeed != -1) sb.append(" lastLinkSpeed=").append(event.lastLinkSpeed); if (event.supplicantStateChangesBitmask != 0) { - sb.append("\n ").append(supplicantStateChangesBitmaskToString( + sb.append(", ").append(supplicantStateChangesBitmaskToString( event.supplicantStateChangesBitmask)); } if (event.configInfo != null) { - sb.append("\n ").append(configInfoToString(event.configInfo)); + sb.append(", ").append(configInfoToString(event.configInfo)); } return sb.toString(); @@ -2053,7 +2244,7 @@ public class WifiMetrics { } public static final int MAX_STA_EVENTS = 512; - private LinkedList<StaEvent> mStaEventList = new LinkedList<StaEvent>(); + private LinkedList<StaEventWithTime> mStaEventList = new LinkedList<StaEventWithTime>(); private int mLastPollRssi = -127; private int mLastPollLinkSpeed = -1; private int mLastPollFreq = -1; @@ -2085,4 +2276,27 @@ public class WifiMetrics { int count = sia.get(element); sia.put(element, count + 1); } + + private static class StaEventWithTime { + public StaEvent staEvent; + public long wallClockMillis; + + StaEventWithTime(StaEvent event, long wallClockMillis) { + staEvent = event; + this.wallClockMillis = wallClockMillis; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(wallClockMillis); + if (wallClockMillis != 0) { + sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); + } else { + sb.append(" "); + } + sb.append(" ").append(staEventToString(staEvent)); + return sb.toString(); + } + } } diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 973b659d2..5b12a3648 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -227,7 +227,16 @@ public class WifiNative { * Returns an empty ArrayList on failure. */ public ArrayList<ScanDetail> getScanResults() { - return mWificondControl.getScanResults(); + return mWificondControl.getScanResults(WificondControl.SCAN_TYPE_SINGLE_SCAN); + } + + /** + * Fetch the latest scan result from kernel via wificond. + * @return Returns an ArrayList of ScanDetail. + * Returns an empty ArrayList on failure. + */ + public ArrayList<ScanDetail> getPnoScanResults() { + return mWificondControl.getScanResults(WificondControl.SCAN_TYPE_PNO_SCAN); } /** @@ -1536,26 +1545,6 @@ public class WifiNative { } /** - * Set the PNO settings & the network list in HAL to start PNO. - * @param settings PNO settings and network list. - * @param eventHandler Handler to receive notifications back during PNO scan. - * @return true if success, false otherwise - */ - public boolean setPnoList(PnoSettings settings, PnoEventHandler eventHandler) { - Log.e(mTAG, "setPnoList not supported"); - return false; - } - - /** - * Reset the PNO settings in HAL to stop PNO. - * @return true if success, false otherwise - */ - public boolean resetPnoList() { - Log.e(mTAG, "resetPnoList not supported"); - return false; - } - - /** * Start sending the specified keep alive packets periodically. * * @param slot Integer used to identify each request. diff --git a/service/java/com/android/server/wifi/WifiNetworkHistory.java b/service/java/com/android/server/wifi/WifiNetworkHistory.java deleted file mode 100644 index f8457cd8a..000000000 --- a/service/java/com/android/server/wifi/WifiNetworkHistory.java +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wifi; - -import android.content.Context; - -import android.net.wifi.ScanResult; - -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; -import android.net.wifi.WifiSsid; -import android.os.Environment; -import android.os.Process; -import android.text.TextUtils; - -import android.util.Log; - -import com.android.server.net.DelayedDiskWrite; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.text.DateFormat; -import java.util.BitSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Provides an API to read and write the network history from WifiConfigurations to file - * This is largely separate and extra to the supplicant config file. - */ -public class WifiNetworkHistory { - public static final String TAG = "WifiNetworkHistory"; - private static final boolean DBG = true; - private static final boolean VDBG = true; - static final String NETWORK_HISTORY_CONFIG_FILE = Environment.getDataDirectory() - + "/misc/wifi/networkHistory.txt"; - /* Network History Keys */ - private static final String SSID_KEY = "SSID"; - static final String CONFIG_KEY = "CONFIG"; - private static final String CONFIG_BSSID_KEY = "CONFIG_BSSID"; - private static final String CHOICE_KEY = "CHOICE"; - private static final String CHOICE_TIME_KEY = "CHOICE_TIME"; - private static final String LINK_KEY = "LINK"; - private static final String BSSID_KEY = "BSSID"; - private static final String BSSID_KEY_END = "/BSSID"; - private static final String RSSI_KEY = "RSSI"; - private static final String FREQ_KEY = "FREQ"; - private static final String DATE_KEY = "DATE"; - private static final String MILLI_KEY = "MILLI"; - private static final String NETWORK_ID_KEY = "ID"; - private static final String PRIORITY_KEY = "PRIORITY"; - private static final String DEFAULT_GW_KEY = "DEFAULT_GW"; - private static final String AUTH_KEY = "AUTH"; - private static final String BSSID_STATUS_KEY = "BSSID_STATUS"; - private static final String SELF_ADDED_KEY = "SELF_ADDED"; - private static final String FAILURE_KEY = "FAILURE"; - private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD"; - private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION"; - static final String CREATOR_UID_KEY = "CREATOR_UID_KEY"; - private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY"; - private static final String UPDATE_UID_KEY = "UPDATE_UID"; - private static final String FQDN_KEY = "FQDN"; - private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE"; - private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH"; - private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS"; - private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS"; - private static final String NO_INTERNET_ACCESS_EXPECTED_KEY = "NO_INTERNET_ACCESS_EXPECTED"; - private static final String EPHEMERAL_KEY = "EPHEMERAL"; - private static final String USE_EXTERNAL_SCORES_KEY = "USE_EXTERNAL_SCORES"; - private static final String METERED_HINT_KEY = "METERED_HINT"; - private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION"; - private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL"; - private static final String CREATOR_NAME_KEY = "CREATOR_NAME"; - private static final String UPDATE_NAME_KEY = "UPDATE_NAME"; - private static final String USER_APPROVED_KEY = "USER_APPROVED"; - private static final String CREATION_TIME_KEY = "CREATION_TIME"; - private static final String UPDATE_TIME_KEY = "UPDATE_TIME"; - static final String SHARED_KEY = "SHARED"; - private static final String NETWORK_SELECTION_STATUS_KEY = "NETWORK_SELECTION_STATUS"; - private static final String NETWORK_SELECTION_DISABLE_REASON_KEY = - "NETWORK_SELECTION_DISABLE_REASON"; - private static final String HAS_EVER_CONNECTED_KEY = "HAS_EVER_CONNECTED"; - - private static final String SEPARATOR = ": "; - private static final String NL = "\n"; - - protected final DelayedDiskWrite mWriter; - Context mContext; - /* - * Lost config list, whenever we read a config from networkHistory.txt that was not in - * wpa_supplicant.conf - */ - HashSet<String> mLostConfigsDbg = new HashSet<String>(); - - public WifiNetworkHistory(Context c, DelayedDiskWrite writer) { - mContext = c; - mWriter = writer; - } - - /** - * Write network history to file, for configured networks - * - * @param networks List of ConfiguredNetworks to write to NetworkHistory - */ - public void writeKnownNetworkHistory(final List<WifiConfiguration> networks, - final ConcurrentHashMap<Integer, ScanDetailCache> scanDetailCaches, - final Set<String> deletedEphemeralSSIDs) { - - /* Make a copy */ - //final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); - - //for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { - // networks.add(new WifiConfiguration(config)); - //} - - mWriter.write(NETWORK_HISTORY_CONFIG_FILE, new DelayedDiskWrite.Writer() { - public void onWriteCalled(DataOutputStream out) throws IOException { - for (WifiConfiguration config : networks) { - //loge("onWriteCalled write SSID: " + config.SSID); - /* if (config.getLinkProperties() != null) - loge(" lp " + config.getLinkProperties().toString()); - else - loge("attempt config w/o lp"); - */ - NetworkSelectionStatus status = config.getNetworkSelectionStatus(); - if (VDBG) { - int numlink = 0; - if (config.linkedConfigurations != null) { - numlink = config.linkedConfigurations.size(); - } - String disableTime; - if (config.getNetworkSelectionStatus().isNetworkEnabled()) { - disableTime = ""; - } else { - disableTime = "Disable time: " + DateFormat.getInstance().format( - config.getNetworkSelectionStatus().getDisableTime()); - } - logd("saving network history: " + config.configKey() + " gw: " - + config.defaultGwMacAddress + " Network Selection-status: " - + status.getNetworkStatusString() - + disableTime + " ephemeral=" + config.ephemeral - + " choice:" + status.getConnectChoice() - + " link:" + numlink - + " status:" + config.status - + " nid:" + config.networkId - + " hasEverConnected: " + status.getHasEverConnected()); - } - - if (!isValid(config)) { - continue; - } - - if (config.SSID == null) { - if (VDBG) { - logv("writeKnownNetworkHistory trying to write config with null SSID"); - } - continue; - } - if (VDBG) { - logv("writeKnownNetworkHistory write config " + config.configKey()); - } - out.writeUTF(CONFIG_KEY + SEPARATOR + config.configKey() + NL); - - if (config.SSID != null) { - out.writeUTF(SSID_KEY + SEPARATOR + config.SSID + NL); - } - if (config.BSSID != null) { - out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + config.BSSID + NL); - } else { - out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + "null" + NL); - } - if (config.FQDN != null) { - out.writeUTF(FQDN_KEY + SEPARATOR + config.FQDN + NL); - } - - out.writeUTF(PRIORITY_KEY + SEPARATOR + Integer.toString(config.priority) + NL); - out.writeUTF(NETWORK_ID_KEY + SEPARATOR - + Integer.toString(config.networkId) + NL); - out.writeUTF(SELF_ADDED_KEY + SEPARATOR - + Boolean.toString(config.selfAdded) + NL); - out.writeUTF(DID_SELF_ADD_KEY + SEPARATOR - + Boolean.toString(config.didSelfAdd) + NL); - out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY + SEPARATOR - + Integer.toString(config.numNoInternetAccessReports) + NL); - out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY + SEPARATOR - + Boolean.toString(config.validatedInternetAccess) + NL); - out.writeUTF(NO_INTERNET_ACCESS_EXPECTED_KEY + SEPARATOR + - Boolean.toString(config.noInternetAccessExpected) + NL); - out.writeUTF(EPHEMERAL_KEY + SEPARATOR - + Boolean.toString(config.ephemeral) + NL); - out.writeUTF(METERED_HINT_KEY + SEPARATOR - + Boolean.toString(config.meteredHint) + NL); - out.writeUTF(USE_EXTERNAL_SCORES_KEY + SEPARATOR - + Boolean.toString(config.useExternalScores) + NL); - if (config.creationTime != null) { - out.writeUTF(CREATION_TIME_KEY + SEPARATOR + config.creationTime + NL); - } - if (config.updateTime != null) { - out.writeUTF(UPDATE_TIME_KEY + SEPARATOR + config.updateTime + NL); - } - if (config.peerWifiConfiguration != null) { - out.writeUTF(PEER_CONFIGURATION_KEY + SEPARATOR - + config.peerWifiConfiguration + NL); - } - out.writeUTF(SCORER_OVERRIDE_KEY + SEPARATOR - + Integer.toString(config.numScorerOverride) + NL); - out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY + SEPARATOR - + Integer.toString(config.numScorerOverrideAndSwitchedNetwork) + NL); - out.writeUTF(NUM_ASSOCIATION_KEY + SEPARATOR - + Integer.toString(config.numAssociation) + NL); - out.writeUTF(CREATOR_UID_KEY + SEPARATOR - + Integer.toString(config.creatorUid) + NL); - out.writeUTF(CONNECT_UID_KEY + SEPARATOR - + Integer.toString(config.lastConnectUid) + NL); - out.writeUTF(UPDATE_UID_KEY + SEPARATOR - + Integer.toString(config.lastUpdateUid) + NL); - out.writeUTF(CREATOR_NAME_KEY + SEPARATOR - + config.creatorName + NL); - out.writeUTF(UPDATE_NAME_KEY + SEPARATOR - + config.lastUpdateName + NL); - out.writeUTF(USER_APPROVED_KEY + SEPARATOR - + Integer.toString(config.userApproved) + NL); - out.writeUTF(SHARED_KEY + SEPARATOR + Boolean.toString(config.shared) + NL); - String allowedKeyManagementString = - makeString(config.allowedKeyManagement, - WifiConfiguration.KeyMgmt.strings); - out.writeUTF(AUTH_KEY + SEPARATOR - + allowedKeyManagementString + NL); - out.writeUTF(NETWORK_SELECTION_STATUS_KEY + SEPARATOR - + status.getNetworkSelectionStatus() + NL); - out.writeUTF(NETWORK_SELECTION_DISABLE_REASON_KEY + SEPARATOR - + status.getNetworkSelectionDisableReason() + NL); - - if (status.getConnectChoice() != null) { - out.writeUTF(CHOICE_KEY + SEPARATOR + status.getConnectChoice() + NL); - out.writeUTF(CHOICE_TIME_KEY + SEPARATOR - + status.getConnectChoiceTimestamp() + NL); - } - - if (config.linkedConfigurations != null) { - log("writeKnownNetworkHistory write linked " - + config.linkedConfigurations.size()); - - for (String key : config.linkedConfigurations.keySet()) { - out.writeUTF(LINK_KEY + SEPARATOR + key + NL); - } - } - - String macAddress = config.defaultGwMacAddress; - if (macAddress != null) { - out.writeUTF(DEFAULT_GW_KEY + SEPARATOR + macAddress + NL); - } - - if (getScanDetailCache(config, scanDetailCaches) != null) { - for (ScanDetail scanDetail : getScanDetailCache(config, - scanDetailCaches).values()) { - ScanResult result = scanDetail.getScanResult(); - out.writeUTF(BSSID_KEY + SEPARATOR - + result.BSSID + NL); - out.writeUTF(FREQ_KEY + SEPARATOR - + Integer.toString(result.frequency) + NL); - - out.writeUTF(RSSI_KEY + SEPARATOR - + Integer.toString(result.level) + NL); - - out.writeUTF(BSSID_KEY_END + NL); - } - } - if (config.lastFailure != null) { - out.writeUTF(FAILURE_KEY + SEPARATOR + config.lastFailure + NL); - } - out.writeUTF(HAS_EVER_CONNECTED_KEY + SEPARATOR - + Boolean.toString(status.getHasEverConnected()) + NL); - out.writeUTF(NL); - // Add extra blank lines for clarity - out.writeUTF(NL); - out.writeUTF(NL); - } - if (deletedEphemeralSSIDs != null && deletedEphemeralSSIDs.size() > 0) { - for (String ssid : deletedEphemeralSSIDs) { - out.writeUTF(DELETED_EPHEMERAL_KEY); - out.writeUTF(ssid); - out.writeUTF(NL); - } - } - } - }); - } - - /** - * Adds information stored in networkHistory.txt to the given configs. The configs are provided - * as a mapping from configKey to WifiConfiguration, because the WifiConfigurations themselves - * do not contain sufficient information to compute their configKeys until after the information - * that is stored in networkHistory.txt has been added to them. - * - * @param configs mapping from configKey to a WifiConfiguration that contains the information - * information read from wpa_supplicant.conf - */ - public void readNetworkHistory(Map<String, WifiConfiguration> configs, - Map<Integer, ScanDetailCache> scanDetailCaches, - Set<String> deletedEphemeralSSIDs) { - - try (DataInputStream in = - new DataInputStream(new BufferedInputStream( - new FileInputStream(NETWORK_HISTORY_CONFIG_FILE)))) { - - String bssid = null; - String ssid = null; - - int freq = 0; - int status = 0; - long seen = 0; - int rssi = WifiConfiguration.INVALID_RSSI; - String caps = null; - - WifiConfiguration config = null; - while (true) { - String line = in.readUTF(); - if (line == null) { - break; - } - int colon = line.indexOf(':'); - if (colon < 0) { - continue; - } - - String key = line.substring(0, colon).trim(); - String value = line.substring(colon + 1).trim(); - - if (key.equals(CONFIG_KEY)) { - config = configs.get(value); - - // skip reading that configuration data - // since we don't have a corresponding network ID - if (config == null) { - Log.e(TAG, "readNetworkHistory didnt find netid for hash=" - + Integer.toString(value.hashCode()) - + " key: " + value); - mLostConfigsDbg.add(value); - continue; - } else { - // After an upgrade count old connections as owned by system - if (config.creatorName == null || config.lastUpdateName == null) { - config.creatorName = - mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID); - config.lastUpdateName = config.creatorName; - - if (DBG) { - Log.w(TAG, "Upgrading network " + config.networkId - + " to " + config.creatorName); - } - } - } - } else if (config != null) { - NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); - switch (key) { - case SSID_KEY: - if (config.isPasspoint()) { - break; - } - ssid = value; - if (config.SSID != null && !config.SSID.equals(ssid)) { - loge("Error parsing network history file, mismatched SSIDs"); - config = null; //error - ssid = null; - } else { - config.SSID = ssid; - } - break; - case CONFIG_BSSID_KEY: - config.BSSID = value.equals("null") ? null : value; - break; - case FQDN_KEY: - // Check for literal 'null' to be backwards compatible. - config.FQDN = value.equals("null") ? null : value; - break; - case DEFAULT_GW_KEY: - config.defaultGwMacAddress = value; - break; - case SELF_ADDED_KEY: - config.selfAdded = Boolean.parseBoolean(value); - break; - case DID_SELF_ADD_KEY: - config.didSelfAdd = Boolean.parseBoolean(value); - break; - case NO_INTERNET_ACCESS_REPORTS_KEY: - config.numNoInternetAccessReports = Integer.parseInt(value); - break; - case VALIDATED_INTERNET_ACCESS_KEY: - config.validatedInternetAccess = Boolean.parseBoolean(value); - break; - case NO_INTERNET_ACCESS_EXPECTED_KEY: - config.noInternetAccessExpected = Boolean.parseBoolean(value); - break; - case CREATION_TIME_KEY: - config.creationTime = value; - break; - case UPDATE_TIME_KEY: - config.updateTime = value; - break; - case EPHEMERAL_KEY: - config.ephemeral = Boolean.parseBoolean(value); - break; - case METERED_HINT_KEY: - config.meteredHint = Boolean.parseBoolean(value); - break; - case USE_EXTERNAL_SCORES_KEY: - config.useExternalScores = Boolean.parseBoolean(value); - break; - case CREATOR_UID_KEY: - config.creatorUid = Integer.parseInt(value); - break; - case SCORER_OVERRIDE_KEY: - config.numScorerOverride = Integer.parseInt(value); - break; - case SCORER_OVERRIDE_AND_SWITCH_KEY: - config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(value); - break; - case NUM_ASSOCIATION_KEY: - config.numAssociation = Integer.parseInt(value); - break; - case CONNECT_UID_KEY: - config.lastConnectUid = Integer.parseInt(value); - break; - case UPDATE_UID_KEY: - config.lastUpdateUid = Integer.parseInt(value); - break; - case FAILURE_KEY: - config.lastFailure = value; - break; - case PEER_CONFIGURATION_KEY: - config.peerWifiConfiguration = value; - break; - case NETWORK_SELECTION_STATUS_KEY: - int networkStatusValue = Integer.parseInt(value); - // Reset temporarily disabled network status - if (networkStatusValue == - NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED) { - networkStatusValue = - NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; - } - networkStatus.setNetworkSelectionStatus(networkStatusValue); - break; - case NETWORK_SELECTION_DISABLE_REASON_KEY: - networkStatus.setNetworkSelectionDisableReason(Integer.parseInt(value)); - break; - case CHOICE_KEY: - networkStatus.setConnectChoice(value); - break; - case CHOICE_TIME_KEY: - networkStatus.setConnectChoiceTimestamp(Long.parseLong(value)); - break; - case LINK_KEY: - if (config.linkedConfigurations == null) { - config.linkedConfigurations = new HashMap<>(); - } else { - config.linkedConfigurations.put(value, -1); - } - break; - case BSSID_KEY: - status = 0; - ssid = null; - bssid = null; - freq = 0; - seen = 0; - rssi = WifiConfiguration.INVALID_RSSI; - caps = ""; - break; - case RSSI_KEY: - rssi = Integer.parseInt(value); - break; - case FREQ_KEY: - freq = Integer.parseInt(value); - break; - case DATE_KEY: - /* - * when reading the configuration from file we don't update the date - * so as to avoid reading back stale or non-sensical data that would - * depend on network time. - * The date of a WifiConfiguration should only come from actual scan - * result. - * - String s = key.replace(FREQ_KEY, ""); - seen = Integer.getInteger(s); - */ - break; - case BSSID_KEY_END: - if ((bssid != null) && (ssid != null)) { - if (getScanDetailCache(config, scanDetailCaches) != null) { - WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid); - ScanDetail scanDetail = new ScanDetail(wssid, bssid, - caps, rssi, freq, (long) 0, seen); - getScanDetailCache(config, scanDetailCaches).put(scanDetail); - } - } - break; - case DELETED_EPHEMERAL_KEY: - if (!TextUtils.isEmpty(value)) { - deletedEphemeralSSIDs.add(value); - } - break; - case CREATOR_NAME_KEY: - config.creatorName = value; - break; - case UPDATE_NAME_KEY: - config.lastUpdateName = value; - break; - case USER_APPROVED_KEY: - config.userApproved = Integer.parseInt(value); - break; - case SHARED_KEY: - config.shared = Boolean.parseBoolean(value); - break; - case HAS_EVER_CONNECTED_KEY: - networkStatus.setHasEverConnected(Boolean.parseBoolean(value)); - break; - } - } - } - } catch (EOFException e) { - // do nothing - } catch (FileNotFoundException e) { - Log.i(TAG, "readNetworkHistory: no config file, " + e); - } catch (NumberFormatException e) { - Log.e(TAG, "readNetworkHistory: failed to parse, " + e, e); - } catch (IOException e) { - Log.e(TAG, "readNetworkHistory: failed to read, " + e, e); - } - } - - /** - * Ported this out of WifiServiceImpl, I have no idea what it's doing - * <TODO> figure out what/why this is doing - * <TODO> Port it into WifiConfiguration, then remove all the silly business from ServiceImpl - */ - public boolean isValid(WifiConfiguration config) { - if (config.allowedKeyManagement == null) { - return false; - } - if (config.allowedKeyManagement.cardinality() > 1) { - if (config.allowedKeyManagement.cardinality() != 2) { - return false; - } - if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { - return false; - } - if ((!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) - && (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK))) { - return false; - } - } - return true; - } - - private static String makeString(BitSet set, String[] strings) { - StringBuffer buf = new StringBuffer(); - int nextSetBit = -1; - - /* Make sure all set bits are in [0, strings.length) to avoid - * going out of bounds on strings. (Shouldn't happen, but...) */ - set = set.get(0, strings.length); - - while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { - buf.append(strings[nextSetBit].replace('_', '-')).append(' '); - } - - // remove trailing space - if (set.cardinality() > 0) { - buf.setLength(buf.length() - 1); - } - - return buf.toString(); - } - - protected void logv(String s) { - Log.v(TAG, s); - } - protected void logd(String s) { - Log.d(TAG, s); - } - protected void log(String s) { - Log.d(TAG, s); - } - protected void loge(String s) { - loge(s, false); - } - protected void loge(String s, boolean stack) { - if (stack) { - Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() - + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() - + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() - + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); - } else { - Log.e(TAG, s); - } - } - - private ScanDetailCache getScanDetailCache(WifiConfiguration config, - Map<Integer, ScanDetailCache> scanDetailCaches) { - if (config == null || scanDetailCaches == null) return null; - ScanDetailCache cache = scanDetailCaches.get(config.networkId); - if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) { - cache = - new ScanDetailCache( - config, WifiConfigManager.SCAN_CACHE_ENTRIES_MAX_SIZE, - WifiConfigManager.SCAN_CACHE_ENTRIES_TRIM_SIZE); - scanDetailCaches.put(config.networkId, cache); - } - return cache; - } -} diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java index 89068a8cc..071fc07d4 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java @@ -575,15 +575,15 @@ public class WifiNetworkSelector { mLocalLog = localLog; mThresholdQualifiedRssi24 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); mThresholdQualifiedRssi5 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); mThresholdMinimumRssi24 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); + R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz); mThresholdMinimumRssi5 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); + R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz); mEnableAutoJoinWhenAssociated = context.getResources().getBoolean( - R.bool.config_wifi_framework_enable_associated_network_selection); + R.bool.config_wifi_framework_enable_associated_network_selection); mStayOnNetworkMinimumTxRate = context.getResources().getInteger( R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network); mStayOnNetworkMinimumRxRate = context.getResources().getInteger( diff --git a/service/java/com/android/server/wifi/WifiNotificationController.java b/service/java/com/android/server/wifi/WifiNotificationController.java deleted file mode 100644 index 797fd438b..000000000 --- a/service/java/com/android/server/wifi/WifiNotificationController.java +++ /dev/null @@ -1,252 +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.annotation.NonNull; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.TaskStackBuilder; -import android.content.Context; -import android.content.Intent; -import android.database.ContentObserver; -import android.net.wifi.WifiManager; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; - -import com.android.internal.notification.SystemNotificationChannels; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; - -/** - * Takes care of handling the "open wi-fi network available" notification - * @hide - */ -public class WifiNotificationController { - /** - * The icon to show in the 'available networks' notification. This will also - * be the ID of the Notification given to the NotificationManager. - */ - private static final int ICON_NETWORKS_AVAILABLE = - com.android.internal.R.drawable.stat_notify_wifi_in_range; - /** - * When a notification is shown, we wait this amount before possibly showing it again. - */ - private final long NOTIFICATION_REPEAT_DELAY_MS; - - /** Whether the user has set the setting to show the 'available networks' notification. */ - private boolean mSettingEnabled; - - /** - * Observes the user setting to keep {@link #mSettingEnabled} in sync. - */ - private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; - - /** - * The {@link System#currentTimeMillis()} must be at least this value for us - * to show the notification again. - */ - private long mNotificationRepeatTime; - /** - * The Notification object given to the NotificationManager. - */ - private Notification.Builder mNotificationBuilder; - /** - * Whether the notification is being shown, as set by us. That is, if the - * user cancels the notification, we will not receive the callback so this - * will still be true. We only guarantee if this is false, then the - * notification is not showing. - */ - private boolean mNotificationShown; - /** Whether the screen is on or not. */ - private boolean mScreenOn; - - private final Context mContext; - private FrameworkFacade mFrameworkFacade; - - WifiNotificationController(Context context, - Looper looper, - FrameworkFacade framework, - Notification.Builder builder) { - mContext = context; - mFrameworkFacade = framework; - mNotificationBuilder = builder; - - mScreenOn = false; - - // Setting is in seconds - NOTIFICATION_REPEAT_DELAY_MS = mFrameworkFacade.getIntegerSetting(context, - Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000L; - mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver( - new Handler(looper)); - mNotificationEnabledSettingObserver.register(); - } - - /** - * Clears the pending notification. This is called by {@link WifiConnectivityManager} on stop. - * - * @param resetRepeatDelay resets the time delay for repeated notification if true. - */ - public void clearPendingNotification(boolean resetRepeatDelay) { - if (resetRepeatDelay) { - mNotificationRepeatTime = 0; - } - setNotificationVisible(false, 0, false, 0); - } - - private boolean isControllerEnabled() { - return mSettingEnabled && !UserManager.get(mContext) - .hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT); - } - - /** - * If there are open networks, attempt to post an open network notification. - * - * @param availableNetworks Available networks from - * {@link WifiNetworkSelector.NetworkEvaluator#getFilteredScanDetailsForOpenUnsavedNetworks()}. - */ - public void handleScanResults(@NonNull List<ScanDetail> availableNetworks) { - if (!isControllerEnabled()) { - clearPendingNotification(true /* resetRepeatDelay */); - return; - } - if (availableNetworks.isEmpty()) { - clearPendingNotification(false /* resetRepeatDelay */); - return; - } - - // Do not show or update the notification if screen is off. We want to avoid a race that - // could occur between a user picking a network in settings and a network candidate picked - // through network selection, which will happen because screen on triggers a new - // connectivity scan. - if (mNotificationShown || !mScreenOn) { - return; - } - - setNotificationVisible(true, availableNetworks.size(), false, 0); - } - - /** Handles screen state changes. */ - public void handleScreenStateChanged(boolean screenOn) { - mScreenOn = screenOn; - } - - /** - * Display or don't display a notification that there are open Wi-Fi networks. - * @param visible {@code true} if notification should be visible, {@code false} otherwise - * @param numNetworks the number networks seen - * @param force {@code true} to force notification to be shown/not-shown, - * even if it is already shown/not-shown. - * @param delay time in milliseconds after which the notification should be made - * visible or invisible. - */ - private void setNotificationVisible(boolean visible, int numNetworks, boolean force, - int delay) { - - // Since we use auto cancel on the notification, when the - // mNetworksAvailableNotificationShown is true, the notification may - // have actually been canceled. However, when it is false we know - // for sure that it is not being shown (it will not be shown any other - // place than here) - - // If it should be hidden and it is already hidden, then noop - if (!visible && !mNotificationShown && !force) { - return; - } - - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - - Message message; - if (visible) { - - // Not enough time has passed to show the notification again - if (System.currentTimeMillis() < mNotificationRepeatTime) { - return; - } - - if (mNotificationBuilder == null) { - // Cache the Notification builder object. - mNotificationBuilder = new Notification.Builder(mContext, - SystemNotificationChannels.NETWORK_AVAILABLE) - .setWhen(0) - .setSmallIcon(ICON_NETWORKS_AVAILABLE) - .setAutoCancel(true) - .setContentIntent(TaskStackBuilder.create(mContext) - .addNextIntentWithParentStack( - new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)) - .getPendingIntent(0, 0, null, UserHandle.CURRENT)) - .setColor(mContext.getResources().getColor( - com.android.internal.R.color.system_notification_accent_color)); - } - - CharSequence title = mContext.getResources().getQuantityText( - com.android.internal.R.plurals.wifi_available, numNetworks); - CharSequence details = mContext.getResources().getQuantityText( - com.android.internal.R.plurals.wifi_available_detailed, numNetworks); - mNotificationBuilder.setTicker(title); - mNotificationBuilder.setContentTitle(title); - mNotificationBuilder.setContentText(details); - - mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; - - notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, - mNotificationBuilder.build(), UserHandle.ALL); - } else { - notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL); - } - - mNotificationShown = visible; - } - - /** Dump ONA controller state. */ - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("WifiNotificationController: "); - pw.println("mSettingEnabled " + mSettingEnabled); - pw.println("mNotificationRepeatTime " + mNotificationRepeatTime); - pw.println("mNotificationShown " + mNotificationShown); - } - - private class NotificationEnabledSettingObserver extends ContentObserver { - NotificationEnabledSettingObserver(Handler handler) { - super(handler); - } - - public void register() { - mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor( - Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); - mSettingEnabled = getValue(); - } - - @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - mSettingEnabled = getValue(); - clearPendingNotification(true /* resetRepeatDelay */); - } - - private boolean getValue() { - return mFrameworkFacade.getIntegerSetting(mContext, - Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; - } - } -} diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 63f47587a..d2f90e0f4 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -591,7 +591,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public void startScan(ScanSettings settings, WorkSource workSource, String packageName) { enforceChangePermission(); - mLog.trace("startScan uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("startScan uid=%").c(Binder.getCallingUid()).flush(); // Check and throttle background apps for wifi scan. if (isRequestFromBackground(packageName)) { long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L); @@ -683,7 +683,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public String getCurrentNetworkWpsNfcConfigurationToken() { enforceConnectivityInternalPermission(); - mLog.trace("getCurrentNetworkWpsNfcConfigurationToken uid=%") + mLog.info("getCurrentNetworkWpsNfcConfigurationToken uid=%") .c(Binder.getCallingUid()).flush(); // TODO Add private logging for netId b/33807876 return mWifiStateMachine.syncGetCurrentNetworkWpsNfcConfigurationToken(); @@ -713,6 +713,11 @@ public class WifiServiceImpl extends IWifiManager.Stub { } } + private boolean checkNetworkSettingsPermission(int pid, int uid) { + return mContext.checkPermission(android.Manifest.permission.NETWORK_SETTINGS, pid, uid) + == PackageManager.PERMISSION_GRANTED; + } + private void enforceNetworkSettingsPermission() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS, "WifiService"); @@ -777,15 +782,15 @@ public class WifiServiceImpl extends IWifiManager.Stub { enforceChangePermission(); Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + ", package=" + packageName); - mLog.trace("setWifiEnabled package=% uid=% enable=%").c(packageName) + mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName) .c(Binder.getCallingUid()).c(enable).flush(); - boolean isFromSettings = - mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid()); + boolean isFromSettings = checkNetworkSettingsPermission( + Binder.getCallingPid(), Binder.getCallingUid()); // If Airplane mode is enabled, only Settings is allowed to toggle Wifi if (mSettingsStore.isAirplaneModeOn() && !isFromSettings) { - mLog.trace("setWifiEnabled in Airplane mode: only Settings can enable wifi").flush(); + mLog.info("setWifiEnabled in Airplane mode: only Settings can enable wifi").flush(); return false; } @@ -794,7 +799,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { mWifiStateMachine.syncGetWifiApState() != WifiManager.WIFI_AP_STATE_DISABLED; if (apEnabled && !isFromSettings) { - mLog.trace("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush(); + mLog.info("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush(); return false; } @@ -847,7 +852,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public int getWifiEnabledState() { enforceAccessPermission(); - mLog.trace("getWifiEnabledState uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getWifiEnabledState uid=%").c(Binder.getCallingUid()).flush(); return mWifiStateMachine.syncGetWifiState(); } @@ -862,7 +867,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { enforceChangePermission(); mWifiPermissionsUtil.enforceTetherChangePermission(mContext); - mLog.trace("setWifiApEnabled uid=% enable=%").c(Binder.getCallingUid()).c(enabled).flush(); + mLog.info("setWifiApEnabled uid=% enable=%").c(Binder.getCallingUid()).c(enabled).flush(); if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) { throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user."); @@ -888,7 +893,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public int getWifiApEnabledState() { enforceAccessPermission(); - mLog.trace("getWifiApEnabledState uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getWifiApEnabledState uid=%").c(Binder.getCallingUid()).flush(); return mWifiStateMachine.syncGetWifiApState(); } @@ -963,7 +968,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { } break; default: - mLog.trace("updateInterfaceIpStateInternal: unknown mode %").c(mode).flush(); + mLog.warn("updateInterfaceIpStateInternal: unknown mode %").c(mode).flush(); } } } @@ -979,7 +984,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { // NETWORK_STACK is a signature only permission. enforceNetworkStackPermission(); - mLog.trace("startSoftAp uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("startSoftAp uid=%").c(Binder.getCallingUid()).flush(); synchronized (mLocalOnlyHotspotRequests) { // If a tethering request comes in while we have LOHS running (or requested), call stop @@ -1023,7 +1028,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { // only permitted callers are allowed to this point - they must have gone through // connectivity service since this method is protected with the NETWORK_STACK PERMISSION - mLog.trace("stopSoftAp uid=%").c(Binder.getCallingUid()).flush(); + 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 @@ -1212,17 +1217,17 @@ public class WifiServiceImpl extends IWifiManager.Stub { return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; } } catch (RemoteException e) { - mLog.trace("RemoteException during isAppForeground when calling startLOHS"); + mLog.warn("RemoteException during isAppForeground when calling startLOHS"); return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; } - mLog.trace("startLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); + mLog.info("startLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); synchronized (mLocalOnlyHotspotRequests) { // check if we are currently tethering if (mIfaceIpModes.contains(WifiManager.IFACE_IP_MODE_TETHERED)) { // Tethering is enabled, cannot start LocalOnlyHotspot - mLog.trace("Cannot start localOnlyHotspot when WiFi Tethering is active."); + mLog.info("Cannot start localOnlyHotspot when WiFi Tethering is active."); return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; } @@ -1272,7 +1277,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); - mLog.trace("stopLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); + mLog.info("stopLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); synchronized (mLocalOnlyHotspotRequests) { // was the caller already registered? check request tracker - return false if not @@ -1360,7 +1365,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { throw new SecurityException("App not allowed to read or update stored WiFi Ap config " + "(uid = " + uid + ")"); } - mLog.trace("getWifiApConfiguration uid=%").c(uid).flush(); + mLog.info("getWifiApConfiguration uid=%").c(uid).flush(); return mWifiStateMachine.syncGetWifiApConfiguration(); } @@ -1379,7 +1384,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { throw new SecurityException("App not allowed to read or update stored WiFi AP config " + "(uid = " + uid + ")"); } - mLog.trace("setWifiApConfiguration uid=%").c(uid).flush(); + mLog.info("setWifiApConfiguration uid=%").c(uid).flush(); if (wifiConfig == null) return; if (isValid(wifiConfig)) { @@ -1395,7 +1400,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean isScanAlwaysAvailable() { enforceAccessPermission(); - mLog.trace("isScanAlwaysAvailable uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("isScanAlwaysAvailable uid=%").c(Binder.getCallingUid()).flush(); return mSettingsStore.isScanAlwaysAvailable(); } @@ -1405,7 +1410,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void disconnect() { enforceChangePermission(); - mLog.trace("disconnect uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("disconnect uid=%").c(Binder.getCallingUid()).flush(); mWifiStateMachine.disconnectCommand(); } @@ -1415,8 +1420,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void reconnect() { enforceChangePermission(); - mLog.trace("reconnect uid=%").c(Binder.getCallingUid()).flush(); - mWifiStateMachine.reconnectCommand(); + mLog.info("reconnect uid=%").c(Binder.getCallingUid()).flush(); + mWifiStateMachine.reconnectCommand(new WorkSource(Binder.getCallingUid())); } /** @@ -1425,7 +1430,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void reassociate() { enforceChangePermission(); - mLog.trace("reassociate uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("reassociate uid=%").c(Binder.getCallingUid()).flush(); mWifiStateMachine.reassociateCommand(); } @@ -1435,7 +1440,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public int getSupportedFeatures() { enforceAccessPermission(); - mLog.trace("getSupportedFeatures uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getSupportedFeatures uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel != null) { return mWifiStateMachine.syncGetSupportedFeatures(mWifiStateMachineChannel); } else { @@ -1447,7 +1452,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void requestActivityInfo(ResultReceiver result) { Bundle bundle = new Bundle(); - mLog.trace("requestActivityInfo uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("requestActivityInfo uid=%").c(Binder.getCallingUid()).flush(); bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, reportActivityInfo()); result.send(0, bundle); } @@ -1458,7 +1463,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public WifiActivityEnergyInfo reportActivityInfo() { enforceAccessPermission(); - mLog.trace("reportActivityInfo uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("reportActivityInfo uid=%").c(Binder.getCallingUid()).flush(); if ((getSupportedFeatures() & WifiManager.WIFI_FEATURE_LINK_LAYER_STATS) == 0) { return null; } @@ -1531,7 +1536,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public ParceledListSlice<WifiConfiguration> getConfiguredNetworks() { enforceAccessPermission(); - mLog.trace("getConfiguredNetworks uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getConfiguredNetworks uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel != null) { List<WifiConfiguration> configs = mWifiStateMachine.syncGetConfiguredNetworks( Binder.getCallingUid(), mWifiStateMachineChannel); @@ -1552,7 +1557,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public ParceledListSlice<WifiConfiguration> getPrivilegedConfiguredNetworks() { enforceReadCredentialPermission(); enforceAccessPermission(); - mLog.trace("getPrivilegedConfiguredNetworks uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getPrivilegedConfiguredNetworks uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel != null) { List<WifiConfiguration> configs = mWifiStateMachine.syncGetPrivilegedConfiguredNetwork(mWifiStateMachineChannel); @@ -1574,7 +1579,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) { enforceAccessPermission(); - mLog.trace("getMatchingWifiConfig uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getMatchingWifiConfig uid=%").c(Binder.getCallingUid()).flush(); if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_PASSPOINT)) { throw new UnsupportedOperationException("Passpoint not enabled"); @@ -1591,7 +1596,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) { enforceAccessPermission(); - mLog.trace("getMatchingOsuProviders uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getMatchingOsuProviders uid=%").c(Binder.getCallingUid()).flush(); if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_PASSPOINT)) { throw new UnsupportedOperationException("Passpoint not enabled"); @@ -1607,7 +1612,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); - mLog.trace("addOrUpdateNetwork uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("addOrUpdateNetwork uid=%").c(Binder.getCallingUid()).flush(); // Previously, this API is overloaded for installing Passpoint profiles. Now // that we have a dedicated API for doing it, redirect the call to the dedicated API. @@ -1678,7 +1683,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean removeNetwork(int netId) { enforceChangePermission(); - mLog.trace("removeNetwork uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("removeNetwork uid=%").c(Binder.getCallingUid()).flush(); // TODO Add private logging for netId b/33807876 if (mWifiStateMachineChannel != null) { return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId); @@ -1699,7 +1704,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); // TODO b/33807876 Log netId - mLog.trace("enableNetwork uid=% disableOthers=%") + mLog.info("enableNetwork uid=% disableOthers=%") .c(Binder.getCallingUid()) .c(disableOthers).flush(); @@ -1722,7 +1727,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public boolean disableNetwork(int netId) { enforceChangePermission(); // TODO b/33807876 Log netId - mLog.trace("disableNetwork uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("disableNetwork uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel != null) { return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId); @@ -1737,14 +1742,14 @@ public class WifiServiceImpl extends IWifiManager.Stub { * @return the Wi-Fi information, contained in {@link WifiInfo}. */ @Override - public WifiInfo getConnectionInfo() { + public WifiInfo getConnectionInfo(String callingPackage) { enforceAccessPermission(); - mLog.trace("getConnectionInfo uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getConnectionInfo uid=%").c(Binder.getCallingUid()).flush(); /* * Make sure we have the latest information, by sending * a status request to the supplicant. */ - return mWifiStateMachine.syncRequestConnectionInfo(); + return mWifiStateMachine.syncRequestConnectionInfo(callingPackage); } /** @@ -1780,7 +1785,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean addOrUpdatePasspointConfiguration(PasspointConfiguration config) { enforceChangePermission(); - mLog.trace("addorUpdatePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("addorUpdatePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush(); if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_PASSPOINT)) { throw new UnsupportedOperationException("Passpoint not enabled"); @@ -1798,7 +1803,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean removePasspointConfiguration(String fqdn) { enforceChangePermission(); - mLog.trace("removePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("removePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush(); if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_PASSPOINT)) { throw new UnsupportedOperationException("Passpoint not enabled"); @@ -1816,7 +1821,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public List<PasspointConfiguration> getPasspointConfigurations() { enforceAccessPermission(); - mLog.trace("getPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush(); if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_PASSPOINT)) { throw new UnsupportedOperationException("Passpoint not enabled"); @@ -1832,7 +1837,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void queryPasspointIcon(long bssid, String fileName) { enforceAccessPermission(); - mLog.trace("queryPasspointIcon uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("queryPasspointIcon uid=%").c(Binder.getCallingUid()).flush(); if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_PASSPOINT)) { throw new UnsupportedOperationException("Passpoint not enabled"); @@ -1847,7 +1852,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { */ @Override public int matchProviderWithCurrentNetwork(String fqdn) { - mLog.trace("matchProviderWithCurrentNetwork uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("matchProviderWithCurrentNetwork uid=%").c(Binder.getCallingUid()).flush(); return mWifiStateMachine.matchProviderWithCurrentNetwork(mWifiStateMachineChannel, fqdn); } @@ -1858,7 +1863,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { */ @Override public void deauthenticateNetwork(long holdoff, boolean ess) { - mLog.trace("deauthenticateNetwork uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("deauthenticateNetwork uid=%").c(Binder.getCallingUid()).flush(); mWifiStateMachine.deauthenticateNetwork(mWifiStateMachineChannel, holdoff, ess); } @@ -1871,7 +1876,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean saveConfiguration() { enforceChangePermission(); - mLog.trace("saveConfiguration uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("saveConfiguration uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel != null) { return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel); } else { @@ -1894,7 +1899,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { Slog.i(TAG, "WifiService trying to set country code to " + countryCode + " with persist set to " + persist); enforceConnectivityInternalPermission(); - mLog.trace("setCountryCode uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("setCountryCode uid=%").c(Binder.getCallingUid()).flush(); final long token = Binder.clearCallingIdentity(); mCountryCode.setCountryCode(countryCode); Binder.restoreCallingIdentity(token); @@ -1909,7 +1914,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public String getCountryCode() { enforceConnectivityInternalPermission(); - mLog.trace("getCountryCode uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getCountryCode uid=%").c(Binder.getCallingUid()).flush(); String country = mCountryCode.getCountryCode(); return country; } @@ -1917,7 +1922,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean isDualBandSupported() { //TODO: Should move towards adding a driver API that checks at runtime - mLog.trace("isDualBandSupported uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("isDualBandSupported uid=%").c(Binder.getCallingUid()).flush(); return mContext.getResources().getBoolean( com.android.internal.R.bool.config_wifi_dual_band_support); } @@ -1932,7 +1937,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Deprecated public DhcpInfo getDhcpInfo() { enforceAccessPermission(); - mLog.trace("getDhcpInfo uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getDhcpInfo uid=%").c(Binder.getCallingUid()).flush(); DhcpResults dhcpResults = mWifiStateMachine.syncGetDhcpResults(); DhcpInfo info = new DhcpInfo(); @@ -2045,7 +2050,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { if (remoteAddress == null) { throw new IllegalArgumentException("remoteAddress cannot be null"); } - mLog.trace("enableTdls uid=% enable=%").c(Binder.getCallingUid()).c(enable).flush(); + mLog.info("enableTdls uid=% enable=%").c(Binder.getCallingUid()).c(enable).flush(); TdlsTaskParams params = new TdlsTaskParams(); params.remoteIpAddress = remoteAddress; params.enable = enable; @@ -2055,7 +2060,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) { - mLog.trace("enableTdlsWithMacAddress uid=% enable=%") + mLog.info("enableTdlsWithMacAddress uid=% enable=%") .c(Binder.getCallingUid()) .c(enable) .flush(); @@ -2074,7 +2079,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public Messenger getWifiServiceMessenger() { enforceAccessPermission(); enforceChangePermission(); - mLog.trace("getWifiServiceMessenger uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getWifiServiceMessenger uid=%").c(Binder.getCallingUid()).flush(); return new Messenger(mClientHandler); } @@ -2085,7 +2090,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public void disableEphemeralNetwork(String SSID) { enforceAccessPermission(); enforceChangePermission(); - mLog.trace("disableEphemeralNetwork uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("disableEphemeralNetwork uid=%").c(Binder.getCallingUid()).flush(); mWifiStateMachine.disableEphemeralNetwork(SSID); } @@ -2321,7 +2326,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) { - mLog.trace("acquireWifiLock uid=% lockMode=%") + mLog.info("acquireWifiLock uid=% lockMode=%") .c(Binder.getCallingUid()) .c(lockMode).flush(); if (mWifiLockManager.acquireWifiLock(lockMode, tag, binder, ws)) { @@ -2333,13 +2338,13 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void updateWifiLockWorkSource(IBinder binder, WorkSource ws) { - mLog.trace("updateWifiLockWorkSource uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("updateWifiLockWorkSource uid=%").c(Binder.getCallingUid()).flush(); mWifiLockManager.updateWifiLockWorkSource(binder, ws); } @Override public boolean releaseWifiLock(IBinder binder) { - mLog.trace("releaseWifiLock uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("releaseWifiLock uid=%").c(Binder.getCallingUid()).flush(); if (mWifiLockManager.releaseWifiLock(binder)) { mWifiController.sendMessage(CMD_LOCKS_CHANGED); return true; @@ -2350,35 +2355,35 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void initializeMulticastFiltering() { enforceMulticastChangePermission(); - mLog.trace("initializeMulticastFiltering uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("initializeMulticastFiltering uid=%").c(Binder.getCallingUid()).flush(); mWifiMulticastLockManager.initializeFiltering(); } @Override public void acquireMulticastLock(IBinder binder, String tag) { enforceMulticastChangePermission(); - mLog.trace("acquireMulticastLock uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("acquireMulticastLock uid=%").c(Binder.getCallingUid()).flush(); mWifiMulticastLockManager.acquireLock(binder, tag); } @Override public void releaseMulticastLock() { enforceMulticastChangePermission(); - mLog.trace("releaseMulticastLock uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("releaseMulticastLock uid=%").c(Binder.getCallingUid()).flush(); mWifiMulticastLockManager.releaseLock(); } @Override public boolean isMulticastEnabled() { enforceAccessPermission(); - mLog.trace("isMulticastEnabled uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("isMulticastEnabled uid=%").c(Binder.getCallingUid()).flush(); return mWifiMulticastLockManager.isMulticastEnabled(); } @Override public void enableVerboseLogging(int verbose) { enforceAccessPermission(); - mLog.trace("enableVerboseLogging uid=% verbose=%") + mLog.info("enableVerboseLogging uid=% verbose=%") .c(Binder.getCallingUid()) .c(verbose).flush(); mFacade.setIntegerSetting( @@ -2398,7 +2403,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public int getVerboseLoggingLevel() { enforceAccessPermission(); - mLog.trace("getVerboseLoggingLevel uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getVerboseLoggingLevel uid=%").c(Binder.getCallingUid()).flush(); return mFacade.getIntegerSetting( mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0); } @@ -2406,7 +2411,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void enableAggressiveHandover(int enabled) { enforceAccessPermission(); - mLog.trace("enableAggressiveHandover uid=% enabled=%") + mLog.info("enableAggressiveHandover uid=% enabled=%") .c(Binder.getCallingUid()) .c(enabled) .flush(); @@ -2416,14 +2421,14 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public int getAggressiveHandover() { enforceAccessPermission(); - mLog.trace("getAggressiveHandover uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getAggressiveHandover uid=%").c(Binder.getCallingUid()).flush(); return mWifiStateMachine.getAggressiveHandover(); } @Override public void setAllowScansWithTraffic(int enabled) { enforceAccessPermission(); - mLog.trace("setAllowScansWithTraffic uid=% enabled=%") + mLog.info("setAllowScansWithTraffic uid=% enabled=%") .c(Binder.getCallingUid()) .c(enabled).flush(); mWifiStateMachine.setAllowScansWithTraffic(enabled); @@ -2432,14 +2437,14 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public int getAllowScansWithTraffic() { enforceAccessPermission(); - mLog.trace("getAllowScansWithTraffic uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getAllowScansWithTraffic uid=%").c(Binder.getCallingUid()).flush(); return mWifiStateMachine.getAllowScansWithTraffic(); } @Override public boolean setEnableAutoJoinWhenAssociated(boolean enabled) { enforceChangePermission(); - mLog.trace("setEnableAutoJoinWhenAssociated uid=% enabled=%") + mLog.info("setEnableAutoJoinWhenAssociated uid=% enabled=%") .c(Binder.getCallingUid()) .c(enabled).flush(); return mWifiStateMachine.setEnableAutoJoinWhenAssociated(enabled); @@ -2448,7 +2453,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public boolean getEnableAutoJoinWhenAssociated() { enforceAccessPermission(); - mLog.trace("getEnableAutoJoinWhenAssociated uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getEnableAutoJoinWhenAssociated uid=%").c(Binder.getCallingUid()).flush(); return mWifiStateMachine.getEnableAutoJoinWhenAssociated(); } @@ -2457,7 +2462,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public WifiConnectionStatistics getConnectionStatistics() { enforceAccessPermission(); enforceReadCredentialPermission(); - mLog.trace("getConnectionStatistics uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getConnectionStatistics uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel != null) { return mWifiStateMachine.syncGetConnectionStatistics(mWifiStateMachineChannel); } else { @@ -2469,7 +2474,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void factoryReset() { enforceConnectivityInternalPermission(); - mLog.trace("factoryReset uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("factoryReset uid=%").c(Binder.getCallingUid()).flush(); if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) { return; } @@ -2543,7 +2548,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public Network getCurrentNetwork() { enforceAccessPermission(); - mLog.trace("getCurrentNetwork uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("getCurrentNetwork uid=%").c(Binder.getCallingUid()).flush(); return mWifiStateMachine.getCurrentNetwork(); } @@ -2575,9 +2580,9 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void enableWifiConnectivityManager(boolean enabled) { enforceConnectivityInternalPermission(); - mLog.trace("enableWifiConnectivityManager uid=% enabled=%") - .c(Binder.getCallingUid()) - .c(enabled).flush(); + mLog.info("enableWifiConnectivityManager uid=% enabled=%") + .c(Binder.getCallingUid()) + .c(enabled).flush(); mWifiStateMachine.enableWifiConnectivityManager(enabled); } @@ -2589,7 +2594,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public byte[] retrieveBackupData() { enforceNetworkSettingsPermission(); - mLog.trace("retrieveBackupData uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("retrieveBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel == null) { Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return null; @@ -2634,7 +2639,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { @Override public void restoreBackupData(byte[] data) { enforceNetworkSettingsPermission(); - mLog.trace("restoreBackupData uid=%").c(Binder.getCallingUid()).flush(); + mLog.info("restoreBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel == null) { Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return; diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index a8bd578c6..ff3aea952 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -31,6 +31,7 @@ import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; import android.Manifest; import android.app.ActivityManager; +import android.app.AppGlobals; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; @@ -82,6 +83,7 @@ import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.p2p.IWifiP2pManager; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.INetworkManagementService; @@ -260,7 +262,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss if (mVerboseLoggingEnabled) { Log.e(TAG, "onRssiThresholdBreach event. Cur Rssi = " + curRssi); } - sendMessage(CMD_RSSI_THRESHOLD_BREACH, curRssi); + sendMessage(CMD_RSSI_THRESHOLD_BREACHED, curRssi); } public void processRssiThreshold(byte curRssi, int reason) { @@ -278,7 +280,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // the rssi thresholds and raised event to host. This would be eggregious if this // value is invalid mWifiInfo.setRssi(curRssi); - updateCapabilities(getCurrentWifiConfiguration()); + updateCapabilities(); int ret = startRssiMonitoringOffload(maxRssi, minRssi); Log.d(TAG, "Re-program RSSI thresholds for " + smToString(reason) + ": [" + minRssi + ", " + maxRssi + "], curRssi=" + curRssi + " ret=" + ret); @@ -364,7 +366,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss private final Object mDhcpResultsLock = new Object(); private DhcpResults mDhcpResults; - // NOTE: Do not return to clients - use #getWiFiInfoForUid(int) + // NOTE: Do not return to clients - see syncRequestConnectionInfo() private final WifiInfo mWifiInfo; private NetworkInfo mNetworkInfo; private final NetworkCapabilities mDfltNetworkCapabilities; @@ -691,7 +693,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss static final int CMD_STOP_RSSI_MONITORING_OFFLOAD = BASE + 163; /* used to indicated RSSI threshold breach in hw */ - static final int CMD_RSSI_THRESHOLD_BREACH = BASE + 164; + static final int CMD_RSSI_THRESHOLD_BREACHED = BASE + 164; /* Enable/Disable WifiConnectivityManager */ static final int CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER = BASE + 166; @@ -1320,7 +1322,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss /** * Initiates connection to a network specified by the user/app. This method checks if the - * requesting app holds the WIFI_CONFIG_OVERRIDE permission. + * requesting app holds the NETWORK_SETTINGS permission. * * @param netId Id network to initiate connection. * @param uid UID of the app requesting the connection. @@ -1349,7 +1351,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss logi("connectToUserSelectNetwork already connecting/connected=" + netId); } else { mWifiConnectivityManager.prepareForForcedConnection(netId); - startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY); + startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY); } return true; } @@ -1755,8 +1757,38 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss * * @return a {@link WifiInfo} object containing information about the current connection */ - public WifiInfo syncRequestConnectionInfo() { - return getWiFiInfoForUid(Binder.getCallingUid()); + public WifiInfo syncRequestConnectionInfo(String callingPackage) { + int uid = Binder.getCallingUid(); + WifiInfo result = new WifiInfo(mWifiInfo); + if (uid == Process.myUid()) return result; + boolean hideBssidAndSsid = true; + result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS); + + IPackageManager packageManager = AppGlobals.getPackageManager(); + + try { + if (packageManager.checkUidPermission(Manifest.permission.LOCAL_MAC_ADDRESS, + uid) == PackageManager.PERMISSION_GRANTED) { + result.setMacAddress(mWifiInfo.getMacAddress()); + } + final WifiConfiguration currentWifiConfiguration = getCurrentWifiConfiguration(); + if (mWifiPermissionsUtil.canAccessFullConnectionInfo( + currentWifiConfiguration, + callingPackage, + uid, + Build.VERSION_CODES.O)) { + hideBssidAndSsid = false; + } + } catch (RemoteException e) { + Log.e(TAG, "Error checking receiver permission", e); + } catch (SecurityException e) { + Log.e(TAG, "Security exception checking receiver permission", e); + } + if (hideBssidAndSsid) { + result.setBSSID(WifiInfo.DEFAULT_MAC_ADDRESS); + result.setSSID(WifiSsid.createFromHex(null)); + } + return result; } public WifiInfo getWifiInfo() { @@ -1844,8 +1876,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss /** * Initiate a reconnection to AP */ - public void reconnectCommand() { - sendMessage(CMD_RECONNECT); + public void reconnectCommand(WorkSource workSource) { + sendMessage(CMD_RECONNECT, workSource); } /** @@ -1884,9 +1916,13 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss public List<WifiConfiguration> syncGetConfiguredNetworks(int uuid, AsyncChannel channel) { Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid); - List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj; - resultMsg.recycle(); - return result; + if (resultMsg == null) { // an error has occurred + return null; + } else { + List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj; + resultMsg.recycle(); + return result; + } } public List<WifiConfiguration> syncGetPrivilegedConfiguredNetwork(AsyncChannel channel) { @@ -2716,7 +2752,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss break; case CMD_START_RSSI_MONITORING_OFFLOAD: case CMD_STOP_RSSI_MONITORING_OFFLOAD: - case CMD_RSSI_THRESHOLD_BREACH: + case CMD_RSSI_THRESHOLD_BREACHED: sb.append(" rssi="); sb.append(Integer.toString(msg.arg1)); sb.append(" thresholds="); @@ -2977,13 +3013,13 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss */ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS); if (newSignalLevel != mLastSignalLevel) { - updateCapabilities(getCurrentWifiConfiguration()); + updateCapabilities(); sendRssiChangeBroadcast(newRssi); } mLastSignalLevel = newSignalLevel; } else { mWifiInfo.setRssi(WifiInfo.INVALID_RSSI); - updateCapabilities(getCurrentWifiConfiguration()); + updateCapabilities(); } if (newLinkSpeed != null) { @@ -3139,29 +3175,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private WifiInfo getWiFiInfoForUid(int uid) { - WifiInfo result = new WifiInfo(mWifiInfo); - if (Binder.getCallingUid() == Process.myUid()) { - return result; - } - - result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS); - - IBinder binder = mFacade.getService("package"); - IPackageManager packageManager = IPackageManager.Stub.asInterface(binder); - - try { - if (packageManager.checkUidPermission(Manifest.permission.LOCAL_MAC_ADDRESS, - uid) == PackageManager.PERMISSION_GRANTED) { - result.setMacAddress(mWifiInfo.getMacAddress()); - } - } catch (RemoteException e) { - Log.e(TAG, "Error checking receiver permission", e); - } - - return result; - } - private void sendLinkConfigurationChangedBroadcast() { Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); @@ -3257,12 +3270,13 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } mWifiInfo.setBSSID(stateChangeResult.BSSID); - mWifiInfo.setSSID(stateChangeResult.wifiSsid); - WifiConfiguration config = getCurrentWifiConfiguration(); + + final WifiConfiguration config = getCurrentWifiConfiguration(); if (config != null) { - // Set meteredHint to true if the access network type of the connecting/connected AP - // is a chargeable public network. + mWifiInfo.setEphemeral(config.ephemeral); + + // Set meteredHint if scan result says network is expensive ScanDetailCache scanDetailCache = mWifiConfigManager.getScanDetailCacheForNetwork( config.networkId); if (scanDetailCache != null) { @@ -3276,15 +3290,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } } - - mWifiInfo.setEphemeral(config.ephemeral); - if (!mWifiInfo.getMeteredHint()) { // don't override the value if already set. - mWifiInfo.setMeteredHint(config.meteredHint); - } } mSupplicantStateTracker.sendMessage(Message.obtain(message)); - return state; } @@ -3424,11 +3432,12 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } /** - * Inform other components (WifiMetrics, WifiDiagnostics, etc.) that the current connection attempt - * has concluded. + * Inform other components (WifiMetrics, WifiDiagnostics, WifiConnectivityManager, etc.) that + * the current connection attempt has concluded. */ private void reportConnectionAttemptEnd(int level2FailureCode, int connectivityFailureCode) { mWifiMetrics.endConnectionEvent(level2FailureCode, connectivityFailureCode); + mWifiConnectivityManager.handleConnectionAttemptEnded(level2FailureCode); switch (level2FailureCode) { case WifiMetrics.ConnectionEvent.FAILURE_NONE: // Ideally, we'd wait until IP reachability has been confirmed. this code falls @@ -3475,11 +3484,20 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiInfo + " got: " + addr); } } + mWifiInfo.setInetAddress(addr); - if (!mWifiInfo.getMeteredHint()) { // don't override the value if already set. - mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint()); - updateCapabilities(getCurrentWifiConfiguration()); + + final WifiConfiguration config = getCurrentWifiConfiguration(); + if (config != null) { + mWifiInfo.setEphemeral(config.ephemeral); + } + + // Set meteredHint if DHCP result says network is metered + if (dhcpResults.hasMeteredHint()) { + mWifiInfo.setMeteredHint(true); } + + updateCapabilities(config); } private void handleSuccessfulIpConfiguration() { @@ -3965,9 +3983,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss deleteNetworkConfigAndSendReply(message, true); break; case WifiManager.SAVE_NETWORK: - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.BUSY); + saveNetworkConfigAndSendReply(message); break; case WifiManager.START_WPS: replyToMessage(message, WifiManager.WPS_FAILED, @@ -4103,11 +4119,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_CLIENT_INTERFACE_BINDER_DEATH: Log.e(TAG, "wificond died unexpectedly. Triggering recovery"); mWifiMetrics.incrementNumWificondCrashes(); + mWifiDiagnostics.captureBugReportData( + WifiDiagnostics.REPORT_REASON_WIFICOND_CRASH); mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFICOND_CRASH); break; case CMD_VENDOR_HAL_HWBINDER_DEATH: Log.e(TAG, "Vendor HAL died unexpectedly. Triggering recovery"); mWifiMetrics.incrementNumHalCrashes(); + mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_HAL_CRASH); mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_HAL_CRASH); break; case CMD_DIAGS_CONNECT_TIMEOUT: @@ -4260,10 +4279,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mLastSignalLevel = -1; mWifiInfo.setMacAddress(mWifiNative.getMacAddress()); - // Attempt to migrate data out of legacy store. - if (!mWifiConfigManager.migrateFromLegacyStore()) { - Log.e(TAG, "Failed to migrate from legacy config store"); - } initializeWpsDetails(); sendSupplicantConnectionChangedBroadcast(true); transitionTo(mSupplicantStartedState); @@ -4523,7 +4538,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mEnableAutoJoinWhenAssociated = allowed; if (!old_state && allowed && mScreenOn && getCurrentState() == mConnectedState) { - mWifiConnectivityManager.forceConnectivityScan(); + mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); } break; case CMD_SELECT_TX_POWER_SCENARIO: @@ -4810,7 +4825,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // Notify PasspointManager of Passpoint network connected event. WifiConfiguration currentNetwork = getCurrentWifiConfiguration(); - if (currentNetwork.isPasspoint()) { + if (currentNetwork != null && currentNetwork.isPasspoint()) { mPasspointManager.onPasspointNetworkConnected(currentNetwork.FQDN); } } @@ -4851,7 +4866,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss return null; } - return scanDetailCache.get(BSSID); + return scanDetailCache.getScanResult(BSSID); } String getCurrentBSSID() { @@ -4941,8 +4956,10 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId, WifiConfiguration.NetworkSelectionStatus .DISABLED_ASSOCIATION_REJECTION); + mWifiConfigManager.setRecentFailureAssociationStatus(mTargetNetworkId, + reasonCode); mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT); - //If rejection occurred while Metrics is tracking a ConnnectionEvent, end it. + // If rejection occurred while Metrics is tracking a ConnnectionEvent, end it. reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, WifiMetricsProto.ConnectionEvent.HLF_NONE); @@ -4970,6 +4987,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } mWifiConfigManager.updateNetworkSelectionStatus( mTargetNetworkId, disableReason); + mWifiConfigManager.clearRecentFailureReason(mTargetNetworkId); //If failure occurred while Metrics is tracking a ConnnectionEvent, end it. reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE, @@ -5143,7 +5161,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mPasspointManager.getMatchingOsuProviders((ScanResult) message.obj)); break; case CMD_RECONNECT: - mWifiConnectivityManager.forceConnectivityScan(); + WorkSource workSource = (WorkSource) message.obj; + mWifiConnectivityManager.forceConnectivityScan(workSource); break; case CMD_REASSOCIATE: lastConnectAttemptTimestamp = mClock.getWallClockMillis(); @@ -5163,7 +5182,23 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_START_CONNECT: /* connect command coming from auto-join */ netId = message.arg1; + int uid = message.arg2; bssid = (String) message.obj; + + synchronized (mWifiReqCountLock) { + if (!hasConnectionRequests()) { + if (mNetworkAgent == null) { + loge("CMD_START_CONNECT but no requests and not connected," + + " bailing"); + break; + } else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { + loge("CMD_START_CONNECT but no requests and connected, but app " + + "does not have sufficient permissions, bailing"); + break; + } + } + } + config = mWifiConfigManager.getConfiguredNetworkWithPassword(netId); logd("CMD_START_CONNECT sup state " + mSupplicantStateTracker.getSupplicantStateName() @@ -5253,41 +5288,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); break; case WifiManager.SAVE_NETWORK: - config = (WifiConfiguration) message.obj; - mWifiConnectionStatistics.numWifiManagerJoinAttempt++; - if (config == null) { - loge("SAVE_NETWORK with null configuration" - + mSupplicantStateTracker.getSupplicantStateName() - + " my state " + getCurrentState().getName()); - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.ERROR); - break; - } - result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); - if (!result.isSuccess()) { - loge("SAVE_NETWORK adding/updating config=" + config + " failed"); - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.ERROR); - break; - } - if (!mWifiConfigManager.enableNetwork( - result.getNetworkId(), false, message.sendingUid)) { - loge("SAVE_NETWORK enabling config=" + config + " failed"); - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.ERROR); - break; - } + result = saveNetworkConfigAndSendReply(message); netId = result.getNetworkId(); - if (mWifiInfo.getNetworkId() == netId) { + if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) { + mWifiConnectionStatistics.numWifiManagerJoinAttempt++; 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."); - startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY); + startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY); } else { if (result.hasProxyChanged()) { log("Reconfiguring proxy on connection"); @@ -5305,8 +5316,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } } - broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); - replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED); break; case WifiManager.FORGET_NETWORK: if (!deleteNetworkConfigAndSendReply(message, true)) { @@ -5389,6 +5398,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case WifiMonitor.NETWORK_CONNECTION_EVENT: if (mVerboseLoggingEnabled) log("Network connection established"); mLastNetworkId = lookupFrameworkNetworkId(message.arg1); + mWifiConfigManager.clearRecentFailureReason(mLastNetworkId); mLastBssid = (String) message.obj; reasonCode = message.arg2; // TODO: This check should not be needed after WifiStateMachinePrime refactor. @@ -5405,7 +5415,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss ScanDetailCache scanDetailCache = mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId); if (scanDetailCache != null && mLastBssid != null) { - ScanResult scanResult = scanDetailCache.get(mLastBssid); + ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid); if (scanResult != null) { mWifiInfo.setFrequency(scanResult.frequency); } @@ -5493,28 +5503,34 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } + public void updateCapabilities() { + updateCapabilities(getCurrentWifiConfiguration()); + } + private void updateCapabilities(WifiConfiguration config) { - NetworkCapabilities networkCapabilities = new NetworkCapabilities(mDfltNetworkCapabilities); - if (config != null) { - if (config.ephemeral) { - networkCapabilities.removeCapability( - NetworkCapabilities.NET_CAPABILITY_TRUSTED); - } else { - networkCapabilities.addCapability( - NetworkCapabilities.NET_CAPABILITY_TRUSTED); - } + final NetworkCapabilities result = new NetworkCapabilities(mDfltNetworkCapabilities); + + if (mWifiInfo != null && !mWifiInfo.isEphemeral()) { + result.addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED); + } else { + result.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED); + } - networkCapabilities.setSignalStrength( - (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI) - ? mWifiInfo.getRssi() - : NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED); + if (mWifiInfo != null && !WifiConfiguration.isMetered(config, mWifiInfo)) { + result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + } else { + result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); } - if (mWifiInfo.getMeteredHint()) { - networkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + if (mWifiInfo != null && mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI) { + result.setSignalStrength(mWifiInfo.getRssi()); + } else { + result.setSignalStrength(NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED); } - mNetworkAgent.sendNetworkCapabilities(networkCapabilities); + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkCapabilities(result); + } } /** @@ -5807,8 +5823,15 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NONE, WifiMetricsProto.ConnectionEvent.HLF_NONE); - sendConnectedState(); - transitionTo(mConnectedState); + if (getCurrentWifiConfiguration() == null) { + // The current config may have been removed while we were connecting, + // trigger a disconnect to clear up state. + mWifiNative.disconnect(); + transitionTo(mDisconnectingState); + } else { + sendConnectedState(); + transitionTo(mConnectedState); + } break; case CMD_IP_CONFIGURATION_LOST: // Get Link layer stats so that we get fresh tx packet counters. @@ -5958,7 +5981,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss ScanDetailCache scanDetailCache = mWifiConfigManager .getScanDetailCacheForNetwork(config.networkId); if (scanDetailCache != null) { - ScanResult scanResult = scanDetailCache.get(mLastBssid); + ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid); if (scanResult != null) { mWifiInfo.setFrequency(scanResult.frequency); } @@ -5968,13 +5991,16 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } break; case CMD_START_RSSI_MONITORING_OFFLOAD: - case CMD_RSSI_THRESHOLD_BREACH: + case CMD_RSSI_THRESHOLD_BREACHED: byte currRssi = (byte) message.arg1; processRssiThreshold(currRssi, message.what); break; case CMD_STOP_RSSI_MONITORING_OFFLOAD: stopRssiMonitoringOffload(); break; + case CMD_RECONNECT: + log(" Ignore CMD_RECONNECT request because wifi is already connected"); + break; case CMD_RESET_SIM_NETWORKS: if (message.arg1 == 0 // sim was removed && mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { @@ -6119,7 +6145,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss WifiConfiguration config = getCurrentWifiConfiguration(); if (shouldEvaluateWhetherToSendExplicitlySelected(config)) { boolean prompt = - mWifiPermissionsUtil.checkConfigOverridePermission(config.lastConnectUid); + mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid); if (mVerboseLoggingEnabled) { log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt); } @@ -6130,7 +6156,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss if (mVerboseLoggingEnabled) { log("explictlySelected acceptUnvalidated=" + config.noInternetAccessExpected); } - mNetworkAgent.explicitlySelected(config.noInternetAccessExpected); + if (mNetworkAgent != null) { + mNetworkAgent.explicitlySelected(config.noInternetAccessExpected); + } } } @@ -6502,13 +6530,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss dstMac = NativeUtil.macAddressToByteArray(dstMacStr); } catch (NullPointerException | IllegalArgumentException e) { loge("Can't find MAC address for next hop to " + pkt.dstAddress); - mNetworkAgent.onPacketKeepaliveEvent(slot, - ConnectivityManager.PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + if (mNetworkAgent != null) { + mNetworkAgent.onPacketKeepaliveEvent(slot, + ConnectivityManager.PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + } break; } pkt.dstMac = dstMac; int result = startWifiIPPacketOffload(slot, pkt, intervalSeconds); - mNetworkAgent.onPacketKeepaliveEvent(slot, result); + if (mNetworkAgent != null) { + mNetworkAgent.onPacketKeepaliveEvent(slot, result); + } break; } default: @@ -6761,7 +6793,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // Ignore intermediate success, wait for full connection break; case WifiMonitor.NETWORK_CONNECTION_EVENT: - if (loadNetworksFromSupplicantAfterWps()) { + Pair<Boolean, Integer> loadResult = loadNetworksFromSupplicantAfterWps(); + boolean success = loadResult.first; + int netId = loadResult.second; + if (success) { + message.arg1 = netId; replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED); } else { replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, @@ -6818,6 +6854,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case WifiManager.CONNECT_NETWORK: case CMD_ENABLE_NETWORK: case CMD_RECONNECT: + log(" Ignore CMD_RECONNECT request because wps is running"); + return HANDLED; case CMD_REASSOCIATE: deferMessage(message); break; @@ -6857,13 +6895,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss /** * Load network config from wpa_supplicant after WPS is complete. + * @return a boolean (true if load was successful) and integer (Network Id of currently + * connected network, can be {@link WifiConfiguration#INVALID_NETWORK_ID} despite + * successful loading, if multiple networks in supplicant) pair. */ - private boolean loadNetworksFromSupplicantAfterWps() { + private Pair<Boolean, Integer> loadNetworksFromSupplicantAfterWps() { Map<String, WifiConfiguration> configs = new HashMap<>(); SparseArray<Map<String, String>> extras = new SparseArray<>(); + int netId = WifiConfiguration.INVALID_NETWORK_ID; if (!mWifiNative.migrateNetworksFromSupplicant(configs, extras)) { loge("Failed to load networks from wpa_supplicant after Wps"); - return false; + return Pair.create(false, WifiConfiguration.INVALID_NETWORK_ID); } for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) { WifiConfiguration config = entry.getValue(); @@ -6874,15 +6916,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss config, mSourceMessage.sendingUid); if (!result.isSuccess()) { loge("Failed to add network after WPS: " + entry.getValue()); - return false; + return Pair.create(false, WifiConfiguration.INVALID_NETWORK_ID); } if (!mWifiConfigManager.enableNetwork( result.getNetworkId(), true, mSourceMessage.sendingUid)) { - loge("Failed to enable network after WPS: " + entry.getValue()); - return false; + Log.wtf(TAG, "Failed to enable network after WPS: " + entry.getValue()); + return Pair.create(false, WifiConfiguration.INVALID_NETWORK_ID); } + netId = result.getNetworkId(); } - return true; + return Pair.create(true, + configs.size() == 1 ? netId : WifiConfiguration.INVALID_NETWORK_ID); } } @@ -7079,14 +7123,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss * Automatically connect to the network specified * * @param networkId ID of the network to connect to + * @param uid UID of the app triggering the connection. * @param bssid BSSID of the network */ - public void startConnectToNetwork(int networkId, String bssid) { - synchronized (mWifiReqCountLock) { - if (hasConnectionRequests()) { - sendMessage(CMD_START_CONNECT, networkId, 0, bssid); - } - } + public void startConnectToNetwork(int networkId, int uid, String bssid) { + sendMessage(CMD_START_CONNECT, networkId, uid, bssid); } /** @@ -7171,6 +7212,43 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } + /** + * 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()); + messageHandlingStatus = 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"); + messageHandlingStatus = 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"); + messageHandlingStatus = 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()) { diff --git a/service/java/com/android/server/wifi/WificondControl.java b/service/java/com/android/server/wifi/WificondControl.java index a877c092d..b6104a8cd 100644 --- a/service/java/com/android/server/wifi/WificondControl.java +++ b/service/java/com/android/server/wifi/WificondControl.java @@ -32,6 +32,7 @@ import android.util.Log; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.NativeUtil; +import com.android.server.wifi.util.ScanResultUtil; import com.android.server.wifi.wificond.ChannelSettings; import com.android.server.wifi.wificond.HiddenNetwork; import com.android.server.wifi.wificond.NativeScanResult; @@ -50,8 +51,16 @@ public class WificondControl { private boolean mVerboseLoggingEnabled = false; private static final String TAG = "WificondControl"; + + /* Get scan results for a single scan */ + public static final int SCAN_TYPE_SINGLE_SCAN = 0; + + /* Get scan results for Pno Scan */ + public static final int SCAN_TYPE_PNO_SCAN = 1; + private WifiInjector mWifiInjector; private WifiMonitor mWifiMonitor; + private final CarrierNetworkConfig mCarrierNetworkConfig; // Cached wificond binder handlers. private IWificond mWificond; @@ -78,9 +87,11 @@ public class WificondControl { } } - WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor) { + WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor, + CarrierNetworkConfig carrierNetworkConfig) { mWifiInjector = wifiInjector; mWifiMonitor = wifiMonitor; + mCarrierNetworkConfig = carrierNetworkConfig; } private class PnoScanEventHandler extends IPnoScanEvent.Stub { @@ -88,12 +99,25 @@ public class WificondControl { public void OnPnoNetworkFound() { Log.d(TAG, "Pno scan result event"); mWifiMonitor.broadcastPnoScanResultEvent(mClientInterfaceName); + mWifiInjector.getWifiMetrics().incrementPnoFoundNetworkEventCount(); } @Override public void OnPnoScanFailed() { Log.d(TAG, "Pno Scan failed event"); - // Nothing to do for now. + 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(); } } @@ -318,14 +342,19 @@ public class WificondControl { * @return Returns an ArrayList of ScanDetail. * Returns an empty ArrayList on failure. */ - public ArrayList<ScanDetail> getScanResults() { + public ArrayList<ScanDetail> getScanResults(int scanType) { ArrayList<ScanDetail> results = new ArrayList<>(); if (mWificondScanner == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return results; } try { - NativeScanResult[] nativeResults = mWificondScanner.getScanResults(); + NativeScanResult[] nativeResults; + if (scanType == SCAN_TYPE_SINGLE_SCAN) { + nativeResults = mWificondScanner.getScanResults(); + } else { + nativeResults = mWificondScanner.getPnoScanResults(); + } for (NativeScanResult result : nativeResults) { WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid); String bssid; @@ -345,15 +374,26 @@ public class WificondControl { new InformationElementUtil.Capabilities(); capabilities.from(ies, result.capability); String flags = capabilities.generateCapabilitiesString(); - NetworkDetail networkDetail = - new NetworkDetail(bssid, ies, null, result.frequency); - - if (!wifiSsid.toString().equals(networkDetail.getTrimmedSSID())) { - Log.e(TAG, "Inconsistent SSID on BSSID: " + bssid); + NetworkDetail networkDetail; + try { + networkDetail = new NetworkDetail(bssid, ies, null, result.frequency); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Illegal argument for scan result with bssid: " + bssid, e); continue; } + ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags, result.signalMbm / 100, result.frequency, result.tsf, ies, null); + // Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi + // network and it uses EAP. + if (ScanResultUtil.isScanResultForEapNetwork(scanDetail.getScanResult()) + && mCarrierNetworkConfig.isCarrierNetwork(wifiSsid.toString())) { + scanDetail.getScanResult().isCarrierAp = true; + scanDetail.getScanResult().carrierApEapType = + mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString()); + scanDetail.getScanResult().carrierName = + mCarrierNetworkConfig.getCarrierName(wifiSsid.toString()); + } results.add(scanDetail); } } catch (RemoteException e1) { @@ -441,9 +481,14 @@ public class WificondControl { } try { - return mWificondScanner.startPnoScan(settings); + boolean success = mWificondScanner.startPnoScan(settings); + mWifiInjector.getWifiMetrics().incrementPnoScanStartAttempCount(); + if (!success) { + mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount(); + } + return success; } catch (RemoteException e1) { - Log.e(TAG, "Failed to stop pno scan due to remote exception"); + Log.e(TAG, "Failed to start pno scan due to remote exception"); } return false; } diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java index 04bf2e009..efeca655b 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java @@ -33,6 +33,7 @@ import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.RouteInfo; +import android.net.wifi.aware.WifiAwareAgentNetworkSpecifier; import android.net.wifi.aware.WifiAwareManager; import android.net.wifi.aware.WifiAwareNetworkSpecifier; import android.net.wifi.aware.WifiAwareUtils; @@ -59,9 +60,11 @@ import java.net.SocketException; import java.util.Arrays; import java.util.Enumeration; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; /** * Manages Aware data-path lifetime: interface creation/deletion, data-path setup and tear-down. @@ -85,8 +88,8 @@ public class WifiAwareDataPathStateManager { private static final int NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL = 1; private final WifiAwareStateManager mMgr; - private final NetworkInterfaceWrapper mNiWrapper = new NetworkInterfaceWrapper(); - private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities(); + public NetworkInterfaceWrapper mNiWrapper = new NetworkInterfaceWrapper(); + private static final NetworkCapabilities sNetworkCapabilitiesFilter = new NetworkCapabilities(); private final Set<String> mInterfaces = new HashSet<>(); private final Map<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> mNetworkRequestsCache = new ArrayMap<>(); @@ -95,7 +98,7 @@ public class WifiAwareDataPathStateManager { private WifiPermissionsWrapper mPermissionsWrapper; private Looper mLooper; private WifiAwareNetworkFactory mNetworkFactory; - private INetworkManagementService mNwService; + public INetworkManagementService mNwService; public WifiAwareDataPathStateManager(WifiAwareStateManager mgr) { mMgr = mgr; @@ -114,19 +117,19 @@ public class WifiAwareDataPathStateManager { mPermissionsWrapper = permissionsWrapper; mLooper = looper; - mNetworkCapabilitiesFilter.clearAll(); - mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE); - mNetworkCapabilitiesFilter + sNetworkCapabilitiesFilter.clearAll(); + sNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE); + sNetworkCapabilitiesFilter .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED); - mNetworkCapabilitiesFilter.setNetworkSpecifier(new MatchAllNetworkSpecifier()); - mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL); - mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL); - mNetworkCapabilitiesFilter.setSignalStrength(NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL); + sNetworkCapabilitiesFilter.setNetworkSpecifier(new MatchAllNetworkSpecifier()); + sNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL); + sNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL); + sNetworkCapabilitiesFilter.setSignalStrength(NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL); - mNetworkFactory = new WifiAwareNetworkFactory(looper, context, mNetworkCapabilitiesFilter); + mNetworkFactory = new WifiAwareNetworkFactory(looper, context, sNetworkCapabilitiesFilter); mNetworkFactory.setScoreFilter(NETWORK_FACTORY_SCORE_AVAIL); mNetworkFactory.register(); @@ -146,6 +149,18 @@ public class WifiAwareDataPathStateManager { return null; } + private Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> + getNetworkRequestByCanonicalDescriptor(CanonicalConnectionInfo cci) { + for (Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> entry : + mNetworkRequestsCache.entrySet()) { + if (entry.getValue().getCanonicalDescriptor().equals(cci)) { + return entry; + } + } + + return null; + } + /** * Create all Aware data-path interfaces which are possible on the device - based on the * capabilities of the firmware. @@ -247,7 +262,7 @@ public class WifiAwareDataPathStateManager { return; } - nnri.state = AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_CONFIRM; + nnri.state = AwareNetworkRequestInformation.STATE_WAIT_FOR_CONFIRM; nnri.ndpId = ndpId; } @@ -309,6 +324,8 @@ public class WifiAwareDataPathStateManager { * - The discovery session (pub/sub ID) must match. * - The peer MAC address (if specified - i.e. non-null) must match. A null peer MAC == * accept (otherwise matching) requests from any peer MAC. + * - The request must be pending (i.e. we could have completed requests for the same + * parameters) */ if (entry.getValue().pubSubId != 0 && entry.getValue().pubSubId != pubSubId) { continue; @@ -319,6 +336,11 @@ public class WifiAwareDataPathStateManager { continue; } + if (entry.getValue().state + != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) { + continue; + } + networkSpecifier = entry.getKey(); nnri = entry.getValue(); break; @@ -411,7 +433,7 @@ public class WifiAwareDataPathStateManager { return; } - nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_CONFIRM; + nnri.state = AwareNetworkRequestInformation.STATE_WAIT_FOR_CONFIRM; } /** @@ -452,18 +474,8 @@ public class WifiAwareDataPathStateManager { AwareNetworkRequestInformation nnri = nnriE.getValue(); // validate state - if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR - && nnri.state != AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_CONFIRM) { - Log.w(TAG, "onDataPathConfirm: INITIATOR in invalid state=" + nnri.state); - mNetworkRequestsCache.remove(networkSpecifier); - if (accept) { - mMgr.endDataPath(ndpId); - } - return networkSpecifier; - } - if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER - && nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_CONFIRM) { - Log.w(TAG, "onDataPathConfirm: RESPONDER in invalid state=" + nnri.state); + if (nnri.state != AwareNetworkRequestInformation.STATE_WAIT_FOR_CONFIRM) { + Log.w(TAG, "onDataPathConfirm: invalid state=" + nnri.state); mNetworkRequestsCache.remove(networkSpecifier); if (accept) { mMgr.endDataPath(ndpId); @@ -472,38 +484,45 @@ public class WifiAwareDataPathStateManager { } if (accept) { - nnri.state = (nnri.networkSpecifier.role - == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) - ? AwareNetworkRequestInformation.STATE_INITIATOR_CONFIRMED - : AwareNetworkRequestInformation.STATE_RESPONDER_CONFIRMED; + nnri.state = AwareNetworkRequestInformation.STATE_CONFIRMED; nnri.peerDataMac = mac; - NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, + NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_NONE, 0, NETWORK_TAG, ""); NetworkCapabilities networkCapabilities = new NetworkCapabilities( - mNetworkCapabilitiesFilter); + sNetworkCapabilitiesFilter); LinkProperties linkProperties = new LinkProperties(); - try { - mNwService.setInterfaceUp(nnri.interfaceName); - mNwService.enableIpv6(nnri.interfaceName); - } catch (Exception e) { // NwService throws runtime exceptions for errors - Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": can't configure network - " - + e); - mMgr.endDataPath(ndpId); - return networkSpecifier; + boolean interfaceUsedByAnotherNdp = isInterfaceUpAndUsedByAnotherNdp(nnri); + if (!interfaceUsedByAnotherNdp) { + try { + mNwService.setInterfaceUp(nnri.interfaceName); + mNwService.enableIpv6(nnri.interfaceName); + } catch (Exception e) { // NwService throws runtime exceptions for errors + Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + + ": can't configure network - " + + e); + mMgr.endDataPath(ndpId); + nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING; + return networkSpecifier; + } + } else { + if (VDBG) { + Log.v(TAG, "onDataPathConfirm: interface already configured: " + + nnri.interfaceName); + } } - if (!mNiWrapper.configureAgentProperties(nnri, networkSpecifier, ndpId, networkInfo, - networkCapabilities, linkProperties)) { + if (!mNiWrapper.configureAgentProperties(nnri, nnri.equivalentSpecifiers, ndpId, + networkInfo, networkCapabilities, linkProperties)) { return networkSpecifier; } nnri.networkAgent = new WifiAwareNetworkAgent(mLooper, mContext, AGENT_TAG_PREFIX + nnri.ndpId, - new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORK_TAG, ""), + new NetworkInfo(ConnectivityManager.TYPE_NONE, 0, NETWORK_TAG, ""), networkCapabilities, linkProperties, NETWORK_FACTORY_SCORE_AVAIL, - networkSpecifier, ndpId); + nnri); nnri.networkAgent.sendNetworkInfo(networkInfo); mAwareMetrics.recordNdpStatus(NanStatusType.SUCCESS, networkSpecifier.isOutOfBand(), @@ -541,13 +560,14 @@ public class WifiAwareDataPathStateManager { return; } - tearDownInterface(nnriE.getValue()); - if (nnriE.getValue().state == AwareNetworkRequestInformation.STATE_RESPONDER_CONFIRMED - || nnriE.getValue().state - == AwareNetworkRequestInformation.STATE_INITIATOR_CONFIRMED) { + tearDownInterfaceIfPossible(nnriE.getValue()); + if (nnriE.getValue().state == AwareNetworkRequestInformation.STATE_CONFIRMED + || nnriE.getValue().state == AwareNetworkRequestInformation.STATE_TERMINATING) { mAwareMetrics.recordNdpSessionDuration(nnriE.getValue().startTimestamp); } mNetworkRequestsCache.remove(nnriE.getKey()); + + mNetworkFactory.tickleConnectivityIfWaiting(); } /** @@ -557,7 +577,7 @@ public class WifiAwareDataPathStateManager { if (VDBG) Log.v(TAG, "onAwareDownCleanupDataPaths"); for (AwareNetworkRequestInformation nnri : mNetworkRequestsCache.values()) { - tearDownInterface(nnri); + tearDownInterfaceIfPossible(nnri); } mNetworkRequestsCache.clear(); } @@ -584,13 +604,26 @@ public class WifiAwareDataPathStateManager { nnri.networkSpecifier.isOutOfBand(), nnri.startTimestamp); mMgr.endDataPath(nnri.ndpId); + nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING; } private class WifiAwareNetworkFactory extends NetworkFactory { + // Request received while waiting for confirmation that a canonically identical data-path + // (NDP) is in the process of being terminated + private boolean mWaitingForTermination = false; + WifiAwareNetworkFactory(Looper looper, Context context, NetworkCapabilities filter) { super(looper, context, NETWORK_TAG, filter); } + public void tickleConnectivityIfWaiting() { + if (mWaitingForTermination) { + if (VDBG) Log.v(TAG, "tickleConnectivityIfWaiting: was waiting!"); + mWaitingForTermination = false; + reevaluateAllRequests(); + } + } + @Override public boolean acceptRequest(NetworkRequest request, int score) { if (VDBG) { @@ -628,7 +661,12 @@ public class WifiAwareDataPathStateManager { if (nnri != null) { if (DBG) { Log.d(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request - + " - already in cache!?"); + + " - already in cache with state=" + nnri.state); + } + + if (nnri.state == AwareNetworkRequestInformation.STATE_TERMINATING) { + mWaitingForTermination = true; + return false; } // seems to happen after a network agent is created - trying to rematch all @@ -644,10 +682,22 @@ public class WifiAwareDataPathStateManager { return false; } - // TODO (b/63635780) support more then a single concurrent NDP - if (mNetworkRequestsCache.size() > 0) { - Log.e(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request - + " - >1 concurrent NDPs aren't supported (yet)."); + // check to see if a canonical version exists + Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> primaryRequest = + getNetworkRequestByCanonicalDescriptor(nnri.getCanonicalDescriptor()); + if (primaryRequest != null) { + if (VDBG) { + Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request + + ", already has a primary request=" + primaryRequest.getKey() + + " with state=" + primaryRequest.getValue().state); + } + + if (primaryRequest.getValue().state + == AwareNetworkRequestInformation.STATE_TERMINATING) { + mWaitingForTermination = true; + } else { + primaryRequest.getValue().updateToSupportNewRequest(networkSpecifier); + } return false; } @@ -676,18 +726,17 @@ public class WifiAwareDataPathStateManager { return; } + if (nnri.state != AwareNetworkRequestInformation.STATE_IDLE) { + if (DBG) { + Log.d(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest=" + + networkRequest + " - already in progress"); + // TODO: understand how/when can be called again/while in progress (seems + // to be related to score re-calculation after a network agent is created) + } + return; + } if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) { - if (nnri.state != AwareNetworkRequestInformation.STATE_INITIATOR_IDLE) { - if (DBG) { - Log.d(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest=" - + networkRequest + " - already in progress"); - // TODO: understand how/when can be called again/while in progress (seems - // to be related to score re-calculation after a network agent is created) - } - return; - } - nnri.interfaceName = selectInterfaceForRequest(nnri); if (nnri.interfaceName == null) { Log.w(TAG, "needNetworkFor: request " + networkSpecifier @@ -704,16 +753,6 @@ public class WifiAwareDataPathStateManager { AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE; nnri.startTimestamp = SystemClock.elapsedRealtime(); } else { - if (nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_IDLE) { - if (DBG) { - Log.d(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest=" - + networkRequest + " - already in progress"); - // TODO: understand how/when can be called again/while in progress (seems - // to be related to score re-calculation after a network agent is created) - } - return; - } - nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST; } } @@ -749,45 +788,52 @@ public class WifiAwareDataPathStateManager { return; } - if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR - && nnri.state - > AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) { - mMgr.endDataPath(nnri.ndpId); - } - if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER - && nnri.state - > AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) { - mMgr.endDataPath(nnri.ndpId); + /* + * Since there's no agent it means we're in the process of setting up the NDP. + * However, it is possible that there were other equivalent requests for this NDP. We + * should keep going in that case. + */ + nnri.removeSupportForRequest(networkSpecifier); + if (nnri.equivalentSpecifiers.isEmpty()) { + if (VDBG) { + Log.v(TAG, "releaseNetworkFor: there are no further requests, networkRequest=" + + networkRequest); + } + if (nnri.ndpId != 0) { // 0 is never a valid ID! + if (VDBG) Log.v(TAG, "releaseNetworkFor: in progress NDP being terminated"); + mMgr.endDataPath(nnri.ndpId); + nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING; + } + } else { + if (VDBG) { + Log.v(TAG, "releaseNetworkFor: equivalent requests exist - not terminating " + + "networkRequest=" + networkRequest); + } } - - // Will get a callback (on both initiator and responder) when data-path actually - // terminated. At that point will inform the agent and will clear the cache. } } private class WifiAwareNetworkAgent extends NetworkAgent { private NetworkInfo mNetworkInfo; - private WifiAwareNetworkSpecifier mNetworkSpecifier; - private int mNdpId; + private AwareNetworkRequestInformation mAwareNetworkRequestInfo; WifiAwareNetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, - WifiAwareNetworkSpecifier networkSpecifier, int ndpId) { + AwareNetworkRequestInformation anri) { super(looper, context, logTag, ni, nc, lp, score); mNetworkInfo = ni; - mNetworkSpecifier = networkSpecifier; - mNdpId = ndpId; + mAwareNetworkRequestInfo = anri; } @Override protected void unwanted() { if (VDBG) { - Log.v(TAG, "WifiAwareNetworkAgent.unwanted: networkSpecifier=" + mNetworkSpecifier - + ", ndpId=" + mNdpId); + Log.v(TAG, "WifiAwareNetworkAgent.unwanted: request=" + mAwareNetworkRequestInfo); } - mMgr.endDataPath(mNdpId); + mMgr.endDataPath(mAwareNetworkRequestInfo.ndpId); + mAwareNetworkRequestInfo.state = AwareNetworkRequestInformation.STATE_TERMINATING; // Will get a callback (on both initiator and responder) when data-path actually // terminated. At that point will inform the agent and will clear the cache. @@ -795,8 +841,8 @@ public class WifiAwareDataPathStateManager { void reconfigureAgentAsDisconnected() { if (VDBG) { - Log.v(TAG, "WifiAwareNetworkAgent.reconfigureAgentAsDisconnected: networkSpecifier=" - + mNetworkSpecifier + ", ndpId=" + mNdpId); + Log.v(TAG, "WifiAwareNetworkAgent.reconfigureAgentAsDisconnected: request=" + + mAwareNetworkRequestInfo); } mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, ""); @@ -804,15 +850,23 @@ public class WifiAwareDataPathStateManager { } } - private void tearDownInterface(AwareNetworkRequestInformation nnri) { - if (VDBG) Log.v(TAG, "tearDownInterface: nnri=" + nnri); + private void tearDownInterfaceIfPossible(AwareNetworkRequestInformation nnri) { + if (VDBG) Log.v(TAG, "tearDownInterfaceIfPossible: nnri=" + nnri); - if (nnri.interfaceName != null && !nnri.interfaceName.isEmpty()) { - try { - mNwService.setInterfaceDown(nnri.interfaceName); - } catch (Exception e) { // NwService throws runtime exceptions for errors - Log.e(TAG, - "tearDownInterface: nnri=" + nnri + ": can't bring interface down - " + e); + if (!TextUtils.isEmpty(nnri.interfaceName)) { + boolean interfaceUsedByAnotherNdp = isInterfaceUpAndUsedByAnotherNdp(nnri); + if (interfaceUsedByAnotherNdp) { + if (VDBG) { + Log.v(TAG, "tearDownInterfaceIfPossible: interfaceName=" + nnri.interfaceName + + ", still in use - not turning down"); + } + } else { + try { + mNwService.setInterfaceDown(nnri.interfaceName); + } catch (Exception e) { // NwService throws runtime exceptions for errors + Log.e(TAG, "tearDownInterfaceIfPossible: nnri=" + nnri + + ": can't bring interface down - " + e); + } } } @@ -821,19 +875,60 @@ public class WifiAwareDataPathStateManager { } } + private boolean isInterfaceUpAndUsedByAnotherNdp(AwareNetworkRequestInformation nri) { + for (AwareNetworkRequestInformation lnri : mNetworkRequestsCache.values()) { + if (lnri == nri) { + continue; + } + + if (nri.interfaceName.equals(lnri.interfaceName) && ( + lnri.state == AwareNetworkRequestInformation.STATE_CONFIRMED + || lnri.state == AwareNetworkRequestInformation.STATE_TERMINATING)) { + return true; + } + } + + return false; + } + /** - * Select one of the existing interfaces for the new network request. + * Select one of the existing interfaces for the new network request. A request is canonical + * (otherwise it wouldn't be executed). * - * TODO: for now there is only a single interface - simply pick it. + * Construct a list of all interfaces currently used to communicate to the peer. The remaining + * interfaces are available for use for this request - if none are left then the request should + * fail (signaled to the caller by returning a null). */ private String selectInterfaceForRequest(AwareNetworkRequestInformation req) { - Iterator<String> it = mInterfaces.iterator(); - if (it.hasNext()) { - return it.next(); + SortedSet<String> potential = new TreeSet<>(mInterfaces); + Set<String> used = new HashSet<>(); + + if (VDBG) { + Log.v(TAG, "selectInterfaceForRequest: req=" + req + ", mNetworkRequestsCache=" + + mNetworkRequestsCache); + } + + for (AwareNetworkRequestInformation nnri : mNetworkRequestsCache.values()) { + if (nnri == req) { + continue; + } + + if (Arrays.equals(req.peerDiscoveryMac, nnri.peerDiscoveryMac)) { + used.add(nnri.interfaceName); + } } - Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - but no interfaces available!"); + if (VDBG) { + Log.v(TAG, "selectInterfaceForRequest: potential=" + potential + ", used=" + used); + } + for (String ifName: potential) { + if (!used.contains(ifName)) { + return ifName; + } + } + + Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - no interfaces available!"); return null; } @@ -853,16 +948,13 @@ public class WifiAwareDataPathStateManager { */ @VisibleForTesting public static class AwareNetworkRequestInformation { - static final int STATE_INITIATOR_IDLE = 100; - static final int STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE = 101; - static final int STATE_INITIATOR_WAIT_FOR_CONFIRM = 102; - static final int STATE_INITIATOR_CONFIRMED = 103; - - static final int STATE_RESPONDER_IDLE = 200; - static final int STATE_RESPONDER_WAIT_FOR_REQUEST = 201; - static final int STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE = 202; - static final int STATE_RESPONDER_WAIT_FOR_CONFIRM = 203; - static final int STATE_RESPONDER_CONFIRMED = 204; + static final int STATE_IDLE = 100; + static final int STATE_WAIT_FOR_CONFIRM = 101; + static final int STATE_CONFIRMED = 102; + static final int STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE = 103; + static final int STATE_RESPONDER_WAIT_FOR_REQUEST = 104; + static final int STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE = 105; + static final int STATE_TERMINATING = 106; public int state; @@ -871,13 +963,55 @@ public class WifiAwareDataPathStateManager { public int pubSubId = 0; public int peerInstanceId = 0; public byte[] peerDiscoveryMac = null; - public int ndpId; + public int ndpId = 0; // 0 is never a valid ID! public byte[] peerDataMac; public WifiAwareNetworkSpecifier networkSpecifier; public long startTimestamp = 0; // request is made (initiator) / get request (responder) public WifiAwareNetworkAgent networkAgent; + /* A collection of specifiers which are equivalent to the current request and are + * supported by it's agent. This list DOES include the original (first) network specifier + * (which is stored separately above). + */ + public Set<WifiAwareNetworkSpecifier> equivalentSpecifiers = new HashSet<>(); + + void updateToSupportNewRequest(WifiAwareNetworkSpecifier ns) { + if (VDBG) Log.v(TAG, "updateToSupportNewRequest: ns=" + ns); + if (equivalentSpecifiers.add(ns) && state == STATE_CONFIRMED) { + if (networkAgent == null) { + Log.wtf(TAG, "updateToSupportNewRequest: null agent in CONFIRMED state!?"); + return; + } + + networkAgent.sendNetworkCapabilities(getNetworkCapabilities()); + } + } + + void removeSupportForRequest(WifiAwareNetworkSpecifier ns) { + if (VDBG) Log.v(TAG, "removeSupportForRequest: ns=" + ns); + equivalentSpecifiers.remove(ns); + + // we will not update the agent: + // 1. this will only get called before the agent is created + // 2. connectivity service does not allow (WTF) updates with reduced capabilities + } + + private NetworkCapabilities getNetworkCapabilities() { + NetworkCapabilities nc = new NetworkCapabilities(sNetworkCapabilitiesFilter); + nc.setNetworkSpecifier(new WifiAwareAgentNetworkSpecifier(equivalentSpecifiers.toArray( + new WifiAwareNetworkSpecifier[equivalentSpecifiers.size()]))); + return nc; + } + + /** + * Returns a canonical descriptor for the network request. + */ + CanonicalConnectionInfo getCanonicalDescriptor() { + return new CanonicalConnectionInfo(peerDiscoveryMac, networkSpecifier.pmk, + networkSpecifier.sessionId, networkSpecifier.passphrase); + } + static AwareNetworkRequestInformation processNetworkSpecifier(WifiAwareNetworkSpecifier ns, WifiAwareStateManager mgr, WifiPermissionsWrapper permissionWrapper) { int uid, pubSubId = 0; @@ -1001,14 +1135,13 @@ public class WifiAwareDataPathStateManager { // create container and populate AwareNetworkRequestInformation nnri = new AwareNetworkRequestInformation(); - nnri.state = (ns.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) - ? AwareNetworkRequestInformation.STATE_INITIATOR_IDLE - : AwareNetworkRequestInformation.STATE_RESPONDER_IDLE; + nnri.state = AwareNetworkRequestInformation.STATE_IDLE; nnri.uid = uid; nnri.pubSubId = pubSubId; nnri.peerInstanceId = peerInstanceId; nnri.peerDiscoveryMac = peerMac; nnri.networkSpecifier = ns; + nnri.equivalentSpecifiers.add(ns); return nnri; } @@ -1025,7 +1158,69 @@ public class WifiAwareDataPathStateManager { ", ndpId=").append(ndpId).append(", peerDataMac=").append( peerDataMac == null ? "" : String.valueOf(HexEncoding.encode(peerDataMac))).append( - ", startTimestamp=").append(startTimestamp); + ", startTimestamp=").append(startTimestamp).append(", equivalentSpecifiers=["); + for (WifiAwareNetworkSpecifier ns: equivalentSpecifiers) { + sb.append(ns.toString()).append(", "); + } + return sb.append("]").toString(); + } + } + + /** + * A canonical (unique) descriptor of the peer connection. + */ + static class CanonicalConnectionInfo { + CanonicalConnectionInfo(byte[] peerDiscoveryMac, byte[] pmk, int sessionId, + String passphrase) { + this.peerDiscoveryMac = peerDiscoveryMac; + this.pmk = pmk; + this.sessionId = sessionId; + this.passphrase = passphrase; + } + + public final byte[] peerDiscoveryMac; + + /* + * Security configuration matching: + * - open: pmk/passphrase = null + * - pmk: pmk != null, passphrase = null + * - passphrase: passphrase != null, sessionId used (==0 for OOB), pmk=null + */ + public final byte[] pmk; + + public final int sessionId; + public final String passphrase; + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(peerDiscoveryMac), Arrays.hashCode(pmk), sessionId, + passphrase); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof CanonicalConnectionInfo)) { + return false; + } + + CanonicalConnectionInfo lhs = (CanonicalConnectionInfo) obj; + + return Arrays.equals(peerDiscoveryMac, lhs.peerDiscoveryMac) && Arrays.equals(pmk, + lhs.pmk) && TextUtils.equals(passphrase, lhs.passphrase) + && sessionId == lhs.sessionId; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("CanonicalConnectionInfo: ["); + sb.append("peerDiscoveryMac=").append(peerDiscoveryMac == null ? "" + : String.valueOf(HexEncoding.encode(peerDiscoveryMac))).append("pmk=").append( + pmk == null ? "" : "*").append("sessionId=").append(sessionId).append( + "passphrase=").append(passphrase == null ? "" : "*").append("]"); return sb.toString(); } } @@ -1040,8 +1235,9 @@ public class WifiAwareDataPathStateManager { * name. Delegated to enable mocking. */ public boolean configureAgentProperties(AwareNetworkRequestInformation nnri, - WifiAwareNetworkSpecifier networkSpecifier, int ndpId, NetworkInfo networkInfo, - NetworkCapabilities networkCapabilities, LinkProperties linkProperties) { + Set<WifiAwareNetworkSpecifier> networkSpecifiers, int ndpId, + NetworkInfo networkInfo, NetworkCapabilities networkCapabilities, + LinkProperties linkProperties) { // find link-local address InetAddress linkLocal = null; NetworkInterface ni; @@ -1051,12 +1247,14 @@ public class WifiAwareDataPathStateManager { Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": can't get network interface - " + e); mMgr.endDataPath(ndpId); + nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING; return false; } if (ni == null) { Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": can't get network interface (null)"); mMgr.endDataPath(ndpId); + nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING; return false; } Enumeration<InetAddress> addresses = ni.getInetAddresses(); @@ -1071,6 +1269,7 @@ public class WifiAwareDataPathStateManager { if (linkLocal == null) { Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": no link local addresses"); mMgr.endDataPath(ndpId); + nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING; return false; } @@ -1078,7 +1277,8 @@ public class WifiAwareDataPathStateManager { networkInfo.setIsAvailable(true); networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); - networkCapabilities.setNetworkSpecifier(networkSpecifier); + networkCapabilities.setNetworkSpecifier(new WifiAwareAgentNetworkSpecifier( + networkSpecifiers.toArray(new WifiAwareNetworkSpecifier[0]))); linkProperties.setInterfaceName(nnri.interfaceName); linkProperties.addLinkAddress(new LinkAddress(linkLocal, 64)); @@ -1095,7 +1295,7 @@ public class WifiAwareDataPathStateManager { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("WifiAwareDataPathStateManager:"); pw.println(" mInterfaces: " + mInterfaces); - pw.println(" mNetworkCapabilitiesFilter: " + mNetworkCapabilitiesFilter); + pw.println(" sNetworkCapabilitiesFilter: " + sNetworkCapabilitiesFilter); pw.println(" mNetworkRequestsCache: " + mNetworkRequestsCache); pw.println(" mNetworkFactory:"); mNetworkFactory.dump(fd, pw, args); diff --git a/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java b/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java index 02eaf5daa..c45c6dcab 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java @@ -320,10 +320,7 @@ public class WifiAwareMetrics { networkRequestCache.values()) { if (anri.state != WifiAwareDataPathStateManager.AwareNetworkRequestInformation - .STATE_INITIATOR_CONFIRMED - && anri.state - != WifiAwareDataPathStateManager.AwareNetworkRequestInformation - .STATE_RESPONDER_CONFIRMED) { + .STATE_CONFIRMED) { continue; // only count completed (up-and-running) NDPs } diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java index 05721afee..2121160bb 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java @@ -167,11 +167,6 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp capabilities.maxSubscribeInterfaceAddresses; frameworkCapabilities.supportedCipherSuites = capabilities.supportedCipherSuites; - // TODO (b/63635780, b/63635857): enable framework support of >1 NDI and >1 NDP per NDI - // Until then: force corresponding capabilities to 1. - frameworkCapabilities.maxNdiInterfaces = 1; - frameworkCapabilities.maxNdpSessions = 1; - mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, frameworkCapabilities); } else { Log.e(TAG, "notifyCapabilitiesResponse: error code=" + status.status + " (" diff --git a/service/java/com/android/server/wifi/aware/WifiAwareRttStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareRttStateManager.java index 74f34b362..9d0441f12 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareRttStateManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareRttStateManager.java @@ -50,6 +50,7 @@ public class WifiAwareRttStateManager { private final SparseArray<WifiAwareClientState> mPendingOperations = new SparseArray<>(); private AsyncChannel mAsyncChannel; + private Context mContext; /** * Initializes the connection to the RTT service. @@ -74,7 +75,7 @@ public class WifiAwareRttStateManager { public void startWithRttService(Context context, Looper looper, IRttManager service) { Messenger messenger; try { - messenger = service.getMessenger(); + messenger = service.getMessenger(null, new int[1]); } catch (RemoteException e) { Log.e(TAG, "start(): not able to getMessenger() of WIFI_RTT_SERVICE"); return; @@ -82,6 +83,7 @@ public class WifiAwareRttStateManager { mAsyncChannel = new AsyncChannel(); mAsyncChannel.connect(context, new AwareRttHandler(looper), messenger); + mContext = context; } private WifiAwareClientState getAndRemovePendingOperationClient(int rangingId) { @@ -125,7 +127,8 @@ public class WifiAwareRttStateManager { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION, + new RttManager.RttClient(mContext.getPackageName())); } else { Log.e(TAG, "Failed to set up channel connection to RTT service"); mAsyncChannel = null; diff --git a/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java index 41eef6960..a2f604686 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java @@ -55,6 +55,7 @@ public class WifiAwareShellCommand extends ShellCommand { for (DelegatedShellCommand dsc: mDelegatedCommands.values()) { dsc.onReset(); } + return 0; } else { DelegatedShellCommand delegatedCmd = null; if (!TextUtils.isEmpty(cmd)) { diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java index 30aa42a26..6ced9486c 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java @@ -204,7 +204,7 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe private volatile Characteristics mCharacteristics = null; private WifiAwareStateMachine mSm; private WifiAwareRttStateManager mRtt; - private WifiAwareDataPathStateManager mDataPathMgr; + public WifiAwareDataPathStateManager mDataPathMgr; private PowerManager mPowerManager; private final SparseArray<WifiAwareClientState> mClients = new SparseArray<>(); diff --git a/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfigParser.java b/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfigParser.java deleted file mode 100644 index 31795f126..000000000 --- a/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfigParser.java +++ /dev/null @@ -1,513 +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.hotspot2; - -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Utility class for parsing legacy (N and older) Passpoint configuration file content - * (/data/misc/wifi/PerProviderSubscription.conf). In N and older, only Release 1 is supported. - * - * This class only retrieve the relevant Release 1 configuration fields that are not backed - * elsewhere. Below are relevant fields: - * - FQDN (used for linking with configuration data stored elsewhere) - * - Friendly Name - * - Roaming Consortium - * - Realm - * - IMSI (for SIM credential) - * - * Below is an example content of a Passpoint configuration file: - * - * tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0) - * 8:MgmtTree+ - * 17:PerProviderSubscription+ - * 4:r1i1+ - * 6:HomeSP+ - * c:FriendlyName=d:Test Provider - * 4:FQDN=8:test.net - * 13:RoamingConsortiumOI=9:1234,5678 - * . - * a:Credential+ - * 10:UsernamePassword+ - * 8:Username=4:user - * 8:Password=4:pass - * - * 9:EAPMethod+ - * 7:EAPType=2:21 - * b:InnerMethod=3:PAP - * . - * . - * 5:Realm=a:boingo.com - * . - * . - * . - * . - * - * Each string is prefixed with a "|StringBytesInHex|:". - * '+' indicates start of a new internal node. - * '.' indicates end of the current internal node. - * '=' indicates "value" of a leaf node. - * - */ -public class LegacyPasspointConfigParser { - private static final String TAG = "LegacyPasspointConfigParser"; - - private static final String TAG_MANAGEMENT_TREE = "MgmtTree"; - private static final String TAG_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription"; - private static final String TAG_HOMESP = "HomeSP"; - private static final String TAG_FQDN = "FQDN"; - private static final String TAG_FRIENDLY_NAME = "FriendlyName"; - private static final String TAG_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI"; - private static final String TAG_CREDENTIAL = "Credential"; - private static final String TAG_REALM = "Realm"; - private static final String TAG_SIM = "SIM"; - private static final String TAG_IMSI = "IMSI"; - - private static final String LONG_ARRAY_SEPARATOR = ","; - private static final String END_OF_INTERNAL_NODE_INDICATOR = "."; - private static final char START_OF_INTERNAL_NODE_INDICATOR = '+'; - private static final char STRING_PREFIX_INDICATOR = ':'; - private static final char STRING_VALUE_INDICATOR = '='; - - /** - * An abstraction for a node within a tree. A node can be an internal node (contained - * children nodes) or a leaf node (contained a String value). - */ - private abstract static class Node { - private final String mName; - Node(String name) { - mName = name; - } - - /** - * @return the name of the node - */ - public String getName() { - return mName; - } - - /** - * Applies for internal node only. - * - * @return the list of children nodes. - */ - public abstract List<Node> getChildren(); - - /** - * Applies for leaf node only. - * - * @return the string value of the node - */ - public abstract String getValue(); - } - - /** - * Class representing an internal node of a tree. It contained a list of child nodes. - */ - private static class InternalNode extends Node { - private final List<Node> mChildren; - InternalNode(String name, List<Node> children) { - super(name); - mChildren = children; - } - - @Override - public List<Node> getChildren() { - return mChildren; - } - - @Override - public String getValue() { - return null; - } - } - - /** - * Class representing a leaf node of a tree. It contained a String type value. - */ - private static class LeafNode extends Node { - private final String mValue; - LeafNode(String name, String value) { - super(name); - mValue = value; - } - - @Override - public List<Node> getChildren() { - return null; - } - - @Override - public String getValue() { - return mValue; - } - } - - public LegacyPasspointConfigParser() {} - - /** - * Parse the legacy Passpoint configuration file content, only retrieve the relevant - * configurations that are not saved elsewhere. - * - * For both N and M, only Release 1 is supported. Most of the configurations are saved - * elsewhere as part of the {@link android.net.wifi.WifiConfiguration} data. - * The configurations needed from the legacy Passpoint configuration file are: - * - * - FQDN - needed to be able to link to the associated {@link WifiConfiguration} data - * - Friendly Name - * - Roaming Consortium OIs - * - Realm - * - IMSI (for SIM credential) - * - * Make this function non-static so that it can be mocked during unit test. - * - * @param fileName The file name of the configuration file - * @return Map of FQDN to {@link LegacyPasspointConfig} - * @throws IOException - */ - public Map<String, LegacyPasspointConfig> parseConfig(String fileName) - throws IOException { - Map<String, LegacyPasspointConfig> configs = new HashMap<>(); - BufferedReader in = new BufferedReader(new FileReader(fileName)); - in.readLine(); // Ignore the first line which contained the header. - - // Convert the configuration data to a management tree represented by a root {@link Node}. - Node root = buildNode(in); - - if (root == null || root.getChildren() == null) { - Log.d(TAG, "Empty configuration data"); - return configs; - } - - // Verify root node name. - if (!TextUtils.equals(TAG_MANAGEMENT_TREE, root.getName())) { - throw new IOException("Unexpected root node: " + root.getName()); - } - - // Process and retrieve the configuration from each PPS (PerProviderSubscription) node. - List<Node> ppsNodes = root.getChildren(); - for (Node ppsNode : ppsNodes) { - LegacyPasspointConfig config = processPpsNode(ppsNode); - configs.put(config.mFqdn, config); - } - return configs; - } - - /** - * Build a {@link Node} from the current line in the buffer. A node can be an internal - * node (ends with '+') or a leaf node. - * - * @param in Input buffer to read data from - * @return {@link Node} representing the current line - * @throws IOException - */ - private static Node buildNode(BufferedReader in) throws IOException { - // Read until non-empty line. - String currentLine = null; - while ((currentLine = in.readLine()) != null) { - if (!currentLine.isEmpty()) { - break; - } - } - - // Return null if EOF is reached. - if (currentLine == null) { - return null; - } - - // Remove the leading and the trailing whitespaces. - currentLine = currentLine.trim(); - - // Check for the internal node terminator. - if (TextUtils.equals(END_OF_INTERNAL_NODE_INDICATOR, currentLine)) { - return null; - } - - // Parse the name-value of the current line. The value will be null if the current line - // is not a leaf node (e.g. line ends with a '+'). - // Each line is encoded in UTF-8. - Pair<String, String> nameValuePair = - parseLine(currentLine.getBytes(StandardCharsets.UTF_8)); - if (nameValuePair.second != null) { - return new LeafNode(nameValuePair.first, nameValuePair.second); - } - - // Parse the children contained under this internal node. - List<Node> children = new ArrayList<>(); - Node child = null; - while ((child = buildNode(in)) != null) { - children.add(child); - } - return new InternalNode(nameValuePair.first, children); - } - - /** - * Process a PPS (PerProviderSubscription) node to retrieve Passpoint configuration data. - * - * @param ppsNode The PPS node to process - * @return {@link LegacyPasspointConfig} - * @throws IOException - */ - private static LegacyPasspointConfig processPpsNode(Node ppsNode) throws IOException { - if (ppsNode.getChildren() == null || ppsNode.getChildren().size() != 1) { - throw new IOException("PerProviderSubscription node should contain " - + "one instance node"); - } - - if (!TextUtils.equals(TAG_PER_PROVIDER_SUBSCRIPTION, ppsNode.getName())) { - throw new IOException("Unexpected name for PPS node: " + ppsNode.getName()); - } - - // Retrieve the PPS instance node. - Node instanceNode = ppsNode.getChildren().get(0); - if (instanceNode.getChildren() == null) { - throw new IOException("PPS instance node doesn't contained any children"); - } - - // Process and retrieve the relevant configurations under the PPS instance node. - LegacyPasspointConfig config = new LegacyPasspointConfig(); - for (Node node : instanceNode.getChildren()) { - switch (node.getName()) { - case TAG_HOMESP: - processHomeSPNode(node, config); - break; - case TAG_CREDENTIAL: - processCredentialNode(node, config); - break; - default: - Log.d(TAG, "Ignore uninterested field under PPS instance: " + node.getName()); - break; - } - } - if (config.mFqdn == null) { - throw new IOException("PPS instance missing FQDN"); - } - return config; - } - - /** - * Process a HomeSP node to retrieve configuration data into the given |config|. - * - * @param homeSpNode The HomeSP node to process - * @param config The config object to fill in the data - * @throws IOException - */ - private static void processHomeSPNode(Node homeSpNode, LegacyPasspointConfig config) - throws IOException { - if (homeSpNode.getChildren() == null) { - throw new IOException("HomeSP node should contain at least one child node"); - } - - for (Node node : homeSpNode.getChildren()) { - switch (node.getName()) { - case TAG_FQDN: - config.mFqdn = getValue(node); - break; - case TAG_FRIENDLY_NAME: - config.mFriendlyName = getValue(node); - break; - case TAG_ROAMING_CONSORTIUM_OI: - config.mRoamingConsortiumOis = parseLongArray(getValue(node)); - break; - default: - Log.d(TAG, "Ignore uninterested field under HomeSP: " + node.getName()); - break; - } - } - } - - /** - * Process a Credential node to retrieve configuration data into the given |config|. - * - * @param credentialNode The Credential node to process - * @param config The config object to fill in the data - * @throws IOException - */ - private static void processCredentialNode(Node credentialNode, - LegacyPasspointConfig config) - throws IOException { - if (credentialNode.getChildren() == null) { - throw new IOException("Credential node should contain at least one child node"); - } - - for (Node node : credentialNode.getChildren()) { - switch (node.getName()) { - case TAG_REALM: - config.mRealm = getValue(node); - break; - case TAG_SIM: - processSimNode(node, config); - break; - default: - Log.d(TAG, "Ignore uninterested field under Credential: " + node.getName()); - break; - } - } - } - - /** - * Process a SIM node to retrieve configuration data into the given |config|. - * - * @param simNode The SIM node to process - * @param config The config object to fill in the data - * @throws IOException - */ - private static void processSimNode(Node simNode, LegacyPasspointConfig config) - throws IOException { - if (simNode.getChildren() == null) { - throw new IOException("SIM node should contain at least one child node"); - } - - for (Node node : simNode.getChildren()) { - switch (node.getName()) { - case TAG_IMSI: - config.mImsi = getValue(node); - break; - default: - Log.d(TAG, "Ignore uninterested field under SIM: " + node.getName()); - break; - } - } - } - - /** - * Parse the given line in the legacy Passpoint configuration file. - * A line can be in the following formats: - * 2:ab+ // internal node - * 2:ab=2:bc // leaf node - * . // end of internal node - * - * @param line The line to parse - * @return name-value pair, a value of null indicates internal node - * @throws IOException - */ - private static Pair<String, String> parseLine(byte[] lineBytes) throws IOException { - Pair<String, Integer> nameIndexPair = parseString(lineBytes, 0); - int currentIndex = nameIndexPair.second; - try { - if (lineBytes[currentIndex] == START_OF_INTERNAL_NODE_INDICATOR) { - return Pair.create(nameIndexPair.first, null); - } - - if (lineBytes[currentIndex] != STRING_VALUE_INDICATOR) { - throw new IOException("Invalid line - missing both node and value indicator: " - + new String(lineBytes, StandardCharsets.UTF_8)); - } - } catch (IndexOutOfBoundsException e) { - throw new IOException("Invalid line - " + e.getMessage() + ": " - + new String(lineBytes, StandardCharsets.UTF_8)); - } - Pair<String, Integer> valueIndexPair = parseString(lineBytes, currentIndex + 1); - return Pair.create(nameIndexPair.first, valueIndexPair.first); - } - - /** - * Parse a string value in the given line from the given start index. - * A string value is in the following format: - * |HexByteLength|:|String| - * - * The length value indicates the number of UTF-8 bytes in hex for the given string. - * - * For example: 3:abc - * - * @param lineBytes The UTF-8 bytes of the line to parse - * @param startIndex The start index from the given line to parse from - * @return Pair of a string value and an index pointed to character after the string value - * @throws IOException - */ - private static Pair<String, Integer> parseString(byte[] lineBytes, int startIndex) - throws IOException { - // Locate the index that separate length and the string value. - int prefixIndex = -1; - for (int i = startIndex; i < lineBytes.length; i++) { - if (lineBytes[i] == STRING_PREFIX_INDICATOR) { - prefixIndex = i; - break; - } - } - if (prefixIndex == -1) { - throw new IOException("Invalid line - missing string prefix: " - + new String(lineBytes, StandardCharsets.UTF_8)); - } - - try { - String lengthStr = new String(lineBytes, startIndex, prefixIndex - startIndex, - StandardCharsets.UTF_8); - int length = Integer.parseInt(lengthStr, 16); - int strStartIndex = prefixIndex + 1; - // The length might account for bytes for the whitespaces, since the whitespaces are - // already trimmed, ignore them. - if ((strStartIndex + length) > lineBytes.length) { - length = lineBytes.length - strStartIndex; - } - return Pair.create( - new String(lineBytes, strStartIndex, length, StandardCharsets.UTF_8), - strStartIndex + length); - } catch (NumberFormatException | IndexOutOfBoundsException e) { - throw new IOException("Invalid line - " + e.getMessage() + ": " - + new String(lineBytes, StandardCharsets.UTF_8)); - } - } - - /** - * Parse a long array from the given string. - * - * @param str The string to parse - * @return long[] - * @throws IOException - */ - private static long[] parseLongArray(String str) - throws IOException { - String[] strArray = str.split(LONG_ARRAY_SEPARATOR); - long[] longArray = new long[strArray.length]; - for (int i = 0; i < longArray.length; i++) { - try { - longArray[i] = Long.parseLong(strArray[i], 16); - } catch (NumberFormatException e) { - throw new IOException("Invalid long integer value: " + strArray[i]); - } - } - return longArray; - } - - /** - * Get the String value of the given node. An IOException will be thrown if the given - * node doesn't contain a String value (internal node). - * - * @param node The node to get the value from - * @return String - * @throws IOException - */ - private static String getValue(Node node) throws IOException { - if (node.getValue() == null) { - throw new IOException("Attempt to retreive value from non-leaf node: " - + node.getName()); - } - return node.getValue(); - } -} diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java index 6ff5ee945..fb8131695 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java @@ -115,6 +115,13 @@ public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEva if (currentNetwork != null && TextUtils.equals(currentNetwork.SSID, ScanResultUtil.createQuotedSSID(bestNetwork.mScanDetail.getSSID()))) { localLog("Staying with current Passpoint network " + currentNetwork.SSID); + + // Update current network with the latest scan info. + mWifiConfigManager.setNetworkCandidateScanResult(currentNetwork.networkId, + bestNetwork.mScanDetail.getScanResult(), 0); + mWifiConfigManager.updateScanDetailForNetwork(currentNetwork.networkId, + bestNetwork.mScanDetail); + connectableNetworks.add(Pair.create(bestNetwork.mScanDetail, currentNetwork)); return currentNetwork; } diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallback.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallback.java index 802f6435c..b7a4b3bc8 100644 --- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallback.java +++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallback.java @@ -30,8 +30,6 @@ import android.util.Log; import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus; import com.android.server.wifi.util.NativeUtil; -import libcore.util.HexEncoding; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -97,7 +95,6 @@ public class SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub byte[] wfdDeviceInfo) { WifiP2pDevice device = new WifiP2pDevice(); device.deviceName = deviceName; - if (deviceName == null) { Log.e(TAG, "Missing device name."); return; @@ -111,8 +108,7 @@ public class SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub } try { - device.primaryDeviceType = new String(HexEncoding.encode( - primaryDeviceType, 0, primaryDeviceType.length)); + device.primaryDeviceType = NativeUtil.wpsDevTypeStringFromByteArray(primaryDeviceType); } catch (Exception e) { Log.e(TAG, "Could not encode device primary type.", e); return; @@ -134,7 +130,6 @@ public class SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub mMonitor.broadcastP2pDeviceFound(mInterface, device); } - /** * Used to indicate that a P2P device has been lost. * diff --git a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java index 0fadd804b..7d0ccbaf6 100644 --- a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java @@ -42,7 +42,6 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb private final WifiNative mWifiNative; private final ChannelHelper mChannelHelper; private final WificondScannerImpl mWificondScannerDelegate; - private final boolean mHalBasedPnoSupported; public HalWifiScannerImpl(Context context, WifiNative wifiNative, WifiMonitor wifiMonitor, Looper looper, Clock clock) { @@ -51,9 +50,6 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb mWificondScannerDelegate = new WificondScannerImpl(context, wifiNative, wifiMonitor, mChannelHelper, looper, clock); - - // We are not going to support HAL ePNO currently. - mHalBasedPnoSupported = false; } @Override @@ -121,38 +117,22 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb @Override public boolean setHwPnoList(WifiNative.PnoSettings settings, WifiNative.PnoEventHandler eventHandler) { - if (mHalBasedPnoSupported) { - return mWifiNative.setPnoList(settings, eventHandler); - } else { - return mWificondScannerDelegate.setHwPnoList(settings, eventHandler); - } + return mWificondScannerDelegate.setHwPnoList(settings, eventHandler); } @Override public boolean resetHwPnoList() { - if (mHalBasedPnoSupported) { - return mWifiNative.resetPnoList(); - } else { - return mWificondScannerDelegate.resetHwPnoList(); - } + return mWificondScannerDelegate.resetHwPnoList(); } @Override public boolean isHwPnoSupported(boolean isConnectedPno) { - if (mHalBasedPnoSupported) { - return true; - } else { - return mWificondScannerDelegate.isHwPnoSupported(isConnectedPno); - } + return mWificondScannerDelegate.isHwPnoSupported(isConnectedPno); } @Override public boolean shouldScheduleBackgroundScanForHwPno() { - if (mHalBasedPnoSupported) { - return true; - } else { - return mWificondScannerDelegate.shouldScheduleBackgroundScanForHwPno(); - } + return mWificondScannerDelegate.shouldScheduleBackgroundScanForHwPno(); } @Override diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index ab2a5dca3..4b8e284cc 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -2129,20 +2129,21 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { pw.println(); pw.println("Latest scan results:"); List<ScanResult> scanResults = mSingleScanStateMachine.getCachedScanResultsAsList(); - long nowMs = System.currentTimeMillis(); + long nowMs = mClock.getElapsedSinceBootMillis(); if (scanResults != null && scanResults.size() != 0) { pw.println(" BSSID Frequency RSSI Age(sec) SSID " + " Flags"); for (ScanResult r : scanResults) { + long timeStampMs = r.timestamp / 1000; String age; - if (r.seen <= 0) { + if (timeStampMs <= 0) { age = "___?___"; - } else if (nowMs < r.seen) { + } else if (nowMs < timeStampMs) { age = " 0.000"; - } else if (r.seen < nowMs - 1000000) { + } else if (timeStampMs < nowMs - 1000000) { age = ">1000.0"; } else { - age = String.format("%3.3f", (nowMs - r.seen) / 1000.0); + age = String.format("%3.3f", (nowMs - timeStampMs) / 1000.0); } String ssid = r.SSID == null ? "" : r.SSID; pw.printf(" %17s %9d %5d %7s %-32s %s\n", diff --git a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java index 84105ee61..ed25e0f67 100644 --- a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java @@ -422,11 +422,22 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call mPendingSingleScanEventHandler = null; } - if ((newScanSettings.backgroundScanActive || newScanSettings.singleScanActive) - && !allFreqs.isEmpty()) { - pauseHwPnoScan(); - Set<Integer> freqs = allFreqs.getScanFreqs(); - boolean success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet); + if (newScanSettings.backgroundScanActive || newScanSettings.singleScanActive) { + boolean success = false; + Set<Integer> freqs; + if (!allFreqs.isEmpty()) { + pauseHwPnoScan(); + freqs = allFreqs.getScanFreqs(); + success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet); + if (!success) { + Log.e(TAG, "Failed to start scan, freqs=" + freqs); + } + } else { + // There is a scan request but no available channels could be scanned for. + // We regard it as a scan failure in this case. + Log.e(TAG, "Failed to start scan because there is " + + "no available channel to scan for"); + } if (success) { // TODO handle scan timeout if (DBG) { @@ -439,7 +450,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS, TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler); } else { - Log.e(TAG, "Failed to start scan, freqs=" + freqs); // indicate scan failure async mEventHandler.post(new Runnable() { public void run() { @@ -538,7 +548,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call // got a scan before we started scanning or after scan was canceled return; } - mNativeScanResults = mWifiNative.getScanResults(); + mNativeScanResults = mWifiNative.getPnoScanResults(); List<ScanResult> hwPnoScanResults = new ArrayList<>(); int numFilteredScanResults = 0; for (int i = 0; i < mNativeScanResults.size(); ++i) { diff --git a/service/java/com/android/server/wifi/util/Matrix.java b/service/java/com/android/server/wifi/util/Matrix.java new file mode 100644 index 000000000..bdf147ede --- /dev/null +++ b/service/java/com/android/server/wifi/util/Matrix.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi.util; + +/** + * Utility for doing basic matix calculations + */ +public class Matrix { + public final int n; + public final int m; + public final double[] mem; + + /** + * Creates a new matrix, initialized to zeros + * + * @param rows - number of rows (n) + * @param cols - number of columns (m) + */ + public Matrix(int rows, int cols) { + n = rows; + m = cols; + mem = new double[rows * cols]; + } + + /** + * Creates a new matrix using the provided array of values + * <p> + * Values are in row-major order. + * + * @param stride is the number of columns. + * @param values is the array of values. + * @throws IllegalArgumentException if length of values array not a multiple of stride + */ + public Matrix(int stride, double[] values) { + n = (values.length + stride - 1) / stride; + m = stride; + mem = values; + if (mem.length != n * m) throw new IllegalArgumentException(); + } + + /** + * Creates a new matrix duplicating the given one + * + * @param that is the source Matrix. + */ + public Matrix(Matrix that) { + n = that.n; + m = that.m; + mem = new double[that.mem.length]; + for (int i = 0; i < mem.length; i++) { + mem[i] = that.mem[i]; + } + } + + /** + * Gets the matrix coefficient from row i, column j + * + * @param i row number + * @param j column number + * @return Coefficient at i,j + * @throws IndexOutOfBoundsException if an index is out of bounds + */ + public double get(int i, int j) { + if (!(0 <= i && i < n && 0 <= j && j < m)) throw new IndexOutOfBoundsException(); + return mem[i * m + j]; + } + + /** + * Store a matrix coefficient in row i, column j + * + * @param i row number + * @param j column number + * @param v Coefficient to store at i,j + * @throws IndexOutOfBoundsException if an index is out of bounds + */ + public void put(int i, int j, double v) { + if (!(0 <= i && i < n && 0 <= j && j < m)) throw new IndexOutOfBoundsException(); + mem[i * m + j] = v; + } + + /** + * Forms the sum of two matrices, this and that + * + * @param that is the other matrix + * @return newly allocated matrix representing the sum of this and that + * @throws IllegalArgumentException if shapes differ + */ + public Matrix plus(Matrix that) { + return plus(that, new Matrix(n, m)); + + } + + /** + * Forms the sum of two matrices, this and that + * + * @param that is the other matrix + * @param result is space to hold the result + * @return result, filled with the matrix sum + * @throws IllegalArgumentException if shapes differ + */ + public Matrix plus(Matrix that, Matrix result) { + if (!(this.n == that.n && this.m == that.m && this.n == result.n && this.m == result.m)) { + throw new IllegalArgumentException(); + } + for (int i = 0; i < mem.length; i++) { + result.mem[i] = this.mem[i] + that.mem[i]; + } + return result; + } + + /** + * Forms the difference of two matrices, this and that + * + * @param that is the other matrix + * @return newly allocated matrix representing the difference of this and that + * @throws IllegalArgumentException if shapes differ + */ + public Matrix minus(Matrix that) { + return minus(that, new Matrix(n, m)); + } + + /** + * Forms the difference of two matrices, this and that + * + * @param that is the other matrix + * @param result is space to hold the result + * @return result, filled with the matrix difference + * @throws IllegalArgumentException if shapes differ + */ + public Matrix minus(Matrix that, Matrix result) { + if (!(this.n == that.n && this.m == that.m && this.n == result.n && this.m == result.m)) { + throw new IllegalArgumentException(); + } + for (int i = 0; i < mem.length; i++) { + result.mem[i] = this.mem[i] - that.mem[i]; + } + return result; + } + + /** + * Forms the matrix product of two matrices, this and that + * + * @param that is the other matrix + * @return newly allocated matrix representing the matrix product of this and that + * @throws IllegalArgumentException if shapes are not conformant + */ + public Matrix dot(Matrix that) { + return dot(that, new Matrix(this.n, that.m)); + } + + /** + * Forms the matrix product of two matrices, this and that + * <p> + * Caller supplies an object to contain the result, as well as scratch space + * + * @param that is the other matrix + * @param result is space to hold the result + * @return result, filled with the matrix product + * @throws IllegalArgumentException if shapes are not conformant + */ + public Matrix dot(Matrix that, Matrix result) { + if (!(this.n == result.n && this.m == that.n && that.m == result.m)) { + throw new IllegalArgumentException(); + } + for (int i = 0; i < n; i++) { + for (int j = 0; j < that.m; j++) { + double s = 0.0; + for (int k = 0; k < m; k++) { + s += this.get(i, k) * that.get(k, j); + } + result.put(i, j, s); + } + } + return result; + } + + /** + * Forms the matrix transpose + * + * @return newly allocated transpose matrix + */ + public Matrix transpose() { + return transpose(new Matrix(m, n)); + } + + /** + * Forms the matrix transpose + * <p> + * Caller supplies an object to contain the result + * + * @param result is space to hold the result + * @return result, filled with the matrix transpose + * @throws IllegalArgumentException if result shape is wrong + */ + public Matrix transpose(Matrix result) { + if (!(this.n == result.m && this.m == result.n)) throw new IllegalArgumentException(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + result.put(j, i, get(i, j)); + } + } + return result; + } + + /** + * Forms the inverse of a square matrix + * + * @return newly allocated matrix representing the matrix inverse + * @throws ArithmeticException if the matrix is not invertible + */ + public Matrix inverse() { + return inverse(new Matrix(n, m), new Matrix(n, 2 * m)); + } + + /** + * Forms the inverse of a square matrix + * + * @param result is space to hold the result + * @param scratch is workspace of dimension n by 2*n + * @return result, filled with the matrix inverse + * @throws ArithmeticException if the matrix is not invertible + * @throws IllegalArgumentException if shape of scratch or result is wrong + */ + public Matrix inverse(Matrix result, Matrix scratch) { + if (!(n == m && n == result.n && m == result.m && n == scratch.n && 2 * m == scratch.m)) { + throw new IllegalArgumentException(); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + scratch.put(i, j, get(i, j)); + scratch.put(i, m + j, i == j ? 1.0 : 0.0); + } + } + + for (int i = 0; i < n; i++) { + int ibest = i; + double vbest = Math.abs(scratch.get(ibest, ibest)); + for (int ii = i + 1; ii < n; ii++) { + double v = Math.abs(scratch.get(ii, i)); + if (v > vbest) { + ibest = ii; + vbest = v; + } + } + if (ibest != i) { + for (int j = 0; j < scratch.m; j++) { + double t = scratch.get(i, j); + scratch.put(i, j, scratch.get(ibest, j)); + scratch.put(ibest, j, t); + } + } + double d = scratch.get(i, i); + if (d == 0.0) throw new ArithmeticException("Singular matrix"); + for (int j = 0; j < scratch.m; j++) { + scratch.put(i, j, scratch.get(i, j) / d); + } + for (int ii = i + 1; ii < n; ii++) { + d = scratch.get(ii, i); + for (int j = 0; j < scratch.m; j++) { + scratch.put(ii, j, scratch.get(ii, j) - d * scratch.get(i, j)); + } + } + } + for (int i = n - 1; i >= 0; i--) { + for (int ii = 0; ii < i; ii++) { + double d = scratch.get(ii, i); + for (int j = 0; j < scratch.m; j++) { + scratch.put(ii, j, scratch.get(ii, j) - d * scratch.get(i, j)); + } + } + } + for (int i = 0; i < result.n; i++) { + for (int j = 0; j < result.m; j++) { + result.put(i, j, scratch.get(i, m + j)); + } + } + return result; + } + + /** + * Tests for equality + */ + @Override + public boolean equals(Object that) { + if (this == that) return true; + if (!(that instanceof Matrix)) return false; + Matrix other = (Matrix) that; + if (n != other.n) return false; + if (m != other.m) return false; + for (int i = 0; i < mem.length; i++) { + if (mem[i] != other.mem[i]) return false; + } + return true; + } + + /** + * Calculates a hash code + */ + @Override + public int hashCode() { + int h = n * 101 + m; + for (int i = 0; i < mem.length; i++) { + h = h * 37 + Double.hashCode(mem[i]); + } + return h; + } + + /** + * Makes a string representation + * + * @return string like "[a, b; c, d]" + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(n * m * 8); + sb.append("["); + for (int i = 0; i < mem.length; i++) { + if (i > 0) sb.append(i % m == 0 ? "; " : ", "); + sb.append(mem[i]); + } + sb.append("]"); + return sb.toString(); + } + +} diff --git a/service/java/com/android/server/wifi/util/NativeUtil.java b/service/java/com/android/server/wifi/util/NativeUtil.java index 07c3f9b38..84f93514c 100644 --- a/service/java/com/android/server/wifi/util/NativeUtil.java +++ b/service/java/com/android/server/wifi/util/NativeUtil.java @@ -30,6 +30,7 @@ import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetDecoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; /** * Provide utility functions for native interfacing modules. @@ -209,13 +210,13 @@ public class NativeUtil { /** * Converts an string to an arraylist of UTF_8 byte values. * These forms are acceptable: - * a) ASCII String encapsulated in quotes, or + * a) UTF-8 String encapsulated in quotes, or * b) Hex string with no delimiters. * * @param str String to be converted. * @throws IllegalArgumentException for null string. */ - public static ArrayList<Byte> hexOrQuotedAsciiStringToBytes(String str) { + public static ArrayList<Byte> hexOrQuotedStringToBytes(String str) { if (str == null) { throw new IllegalArgumentException("null string"); } @@ -231,21 +232,21 @@ public class NativeUtil { /** * Converts an ArrayList<Byte> of UTF_8 byte values to string. * The string will either be: - * a) ASCII String encapsulated in quotes (if all the bytes are ASCII encodeable and non null), + * a) UTF-8 String encapsulated in quotes (if all the bytes are UTF-8 encodeable and non null), * or * b) Hex string with no delimiters. * * @param bytes List of bytes for ssid. * @throws IllegalArgumentException for null bytes. */ - public static String bytesToHexOrQuotedAsciiString(ArrayList<Byte> bytes) { + public static String bytesToHexOrQuotedString(ArrayList<Byte> bytes) { if (bytes == null) { throw new IllegalArgumentException("null ssid bytes"); } byte[] byteArray = byteArrayFromArrayList(bytes); // Check for 0's in the byte stream in which case we cannot convert this into a string. if (!bytes.contains(Byte.valueOf((byte) 0))) { - CharsetDecoder decoder = StandardCharsets.US_ASCII.newDecoder(); + CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); try { CharBuffer decoded = decoder.decode(ByteBuffer.wrap(byteArray)); return "\"" + decoded.toString() + "\""; @@ -258,20 +259,20 @@ public class NativeUtil { /** * Converts an ssid string to an arraylist of UTF_8 byte values. * These forms are acceptable: - * a) ASCII String encapsulated in quotes, or + * a) UTF-8 String encapsulated in quotes, or * b) Hex string with no delimiters. * * @param ssidStr String to be converted. * @throws IllegalArgumentException for null string. */ public static ArrayList<Byte> decodeSsid(String ssidStr) { - return hexOrQuotedAsciiStringToBytes(ssidStr); + return hexOrQuotedStringToBytes(ssidStr); } /** * Converts an ArrayList<Byte> of UTF_8 byte values to ssid string. * The string will either be: - * a) ASCII String encapsulated in quotes (if all the bytes are ASCII encodeable and non null), + * a) UTF-8 String encapsulated in quotes (if all the bytes are UTF-8 encodeable and non null), * or * b) Hex string with no delimiters. * @@ -279,7 +280,7 @@ public class NativeUtil { * @throws IllegalArgumentException for null bytes. */ public static String encodeSsid(ArrayList<Byte> ssidBytes) { - return bytesToHexOrQuotedAsciiString(ssidBytes); + return bytesToHexOrQuotedString(ssidBytes); } /** @@ -330,4 +331,16 @@ public class NativeUtil { } return new String(HexEncoding.encode(bytes)).toLowerCase(); } + + /** + * Converts an 8 byte array to a WPS device type string + * { 0, 1, 2, -1, 4, 5, 6, 7 } --> "1-02FF0405-1543"; + */ + public static String wpsDevTypeStringFromByteArray(byte[] devType) { + byte[] a = devType; + int x = ((a[0] & 0xFF) << 8) | (a[1] & 0xFF); + String y = new String(HexEncoding.encode(Arrays.copyOfRange(devType, 2, 6))); + int z = ((a[6] & 0xFF) << 8) | (a[7] & 0xFF); + return String.format("%d-%s-%d", x, y, z); + } } diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java index c5eea7d70..52c96183e 100644 --- a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java +++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java @@ -17,16 +17,23 @@ package com.android.server.wifi.util; import android.Manifest; +import android.annotation.Nullable; import android.app.AppOpsManager; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.NetworkScoreManager; +import android.net.NetworkScorerAppData; +import android.net.wifi.WifiConfiguration; +import android.os.Binder; import android.os.RemoteException; import android.os.UserManager; import android.provider.Settings; +import android.text.TextUtils; +import com.android.server.wifi.FrameworkFacade; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiLog; import com.android.server.wifi.WifiSettingsStore; @@ -45,6 +52,7 @@ public class WifiPermissionsUtil { private final UserManager mUserManager; private final WifiSettingsStore mSettingsStore; private final NetworkScoreManager mNetworkScoreManager; + private final FrameworkFacade mFrameworkFacade; private WifiLog mLog; public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, @@ -57,6 +65,7 @@ public class WifiPermissionsUtil { mSettingsStore = settingsStore; mLog = wifiInjector.makeLog(TAG); mNetworkScoreManager = networkScoreManager; + mFrameworkFacade = wifiInjector.getFrameworkFacade(); } /** @@ -113,10 +122,29 @@ public class WifiPermissionsUtil { } } + + /** + * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION + * and a corresponding app op is allowed for this package and uid. + * + * @param pkgName PackageName of the application requesting access + * @param uid The uid of the package + */ + public boolean checkCallersLocationPermission(String pkgName, int uid) { + // Coarse Permission implies Fine permission + if ((mWifiPermissionsWrapper.getUidPermission( + Manifest.permission.ACCESS_COARSE_LOCATION, uid) + == PackageManager.PERMISSION_GRANTED) + && checkAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid)) { + return true; + } + return false; + } + /** * API to determine if the caller has permissions to get * scan results. - * @param pkgName Packagename of the application requesting access + * @param pkgName package name of the application requesting access * @param uid The uid of the package * @param minVersion Minimum app API Version number to enforce location permission * @return boolean true or false if permissions is granted @@ -148,7 +176,7 @@ public class WifiPermissionsUtil { } // If the User or profile is current, permission is granted // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. - if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { + if (!canAccessUserProfile(uid)) { mLog.tC("Denied: Profile not permitted"); return false; } @@ -156,6 +184,77 @@ public class WifiPermissionsUtil { } /** + * API to determine if the caller has permissions to get a {@link android.net.wifi.WifiInfo} + * instance containing the SSID and BSSID. + * + * + * @param currentConfig the currently connected WiFi config + * @param pkgName package name of the application requesting access + * @param uid The uid of the package + * @param minVersion Minimum app API Version number to enforce location permission + * @return boolean true if the SSID/BSSID can be sent to the user, false if they + * should be hidden/removed. + */ + public boolean canAccessFullConnectionInfo(@Nullable WifiConfiguration currentConfig, + String pkgName, int uid, int minVersion) throws SecurityException { + mAppOps.checkPackage(uid, pkgName); + + // The User or profile must be current or the uid must + // have INTERACT_ACROSS_USERS_FULL permission. + if (!canAccessUserProfile(uid)) { + mLog.tC("Denied: Profile not permitted"); + return false; + } + + // If the caller has scan result access then they can also see the full connection info. + // Otherwise the caller must be the active use open wifi package and the current config + // must be for an open network. + return canAccessScanResults(pkgName, uid, minVersion) + || isUseOpenWifiPackageWithConnectionInfoAccess(currentConfig, pkgName); + + } + + /** + * Returns true if the given WiFi config is for an open network and the package is the active + * use open wifi app. + */ + private boolean isUseOpenWifiPackageWithConnectionInfoAccess( + @Nullable WifiConfiguration currentConfig, String pkgName) { + + // Access is only granted for open networks. + if (currentConfig == null) { + mLog.tC("Denied: WifiConfiguration is NULL."); + return false; + } + + // Access is only granted for open networks. + if (!currentConfig.isOpenNetwork()) { + mLog.tC("Denied: The current config is not for an open network."); + return false; + } + + // The USE_OPEN_WIFI_PACKAGE can access the full connection info details without + // scan result access. + if (!isUseOpenWifiPackage(pkgName)) { + mLog.tC("Denied: caller is not the current USE_OPEN_WIFI_PACKAGE"); + return false; + } + + return true; + } + + /** + * Returns true if the User or profile is current or the + * uid has the INTERACT_ACROSS_USERS_FULL permission. + */ + private boolean canAccessUserProfile(int uid) { + if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { + return false; + } + return true; + } + + /** * Returns true if the caller holds PEERS_MAC_ADDRESS permission. */ private boolean checkCallerHasPeersMacAddressPermission(int uid) { @@ -172,6 +271,43 @@ public class WifiPermissionsUtil { } /** + * Returns true if the given package is equal to the setting keyed by + * {@link Settings.Global#USE_OPEN_WIFI_PACKAGE} and the NetworkScoreManager + * has the package name set as the use open wifi package. + */ + private boolean isUseOpenWifiPackage(String packageName) { + if (TextUtils.isEmpty(packageName)) { + return false; + } + + // When the setting is enabled it's set to the package name of the use open wifi app. + final String useOpenWifiPkg = + mFrameworkFacade.getStringSetting(mContext, Settings.Global.USE_OPEN_WIFI_PACKAGE); + if (packageName.equals(useOpenWifiPkg)) { + // If the package name matches the setting then also confirm that the scorer is + // active and the package matches the expected use open wifi package from the scorer's + // perspective. The scorer can be active when the use open wifi feature is off so we + // can't rely on this check alone. + // TODO(b/67278755): Refactor this into an API similar to isCallerActiveScorer() + final NetworkScorerAppData appData; + final long token = Binder.clearCallingIdentity(); + try { + appData = mNetworkScoreManager.getActiveScorer(); + } finally { + Binder.restoreCallingIdentity(token); + } + if (appData != null) { + final ComponentName enableUseOpenWifiActivity = + appData.getEnableUseOpenWifiActivity(); + return enableUseOpenWifiActivity != null + && packageName.equals(enableUseOpenWifiActivity.getPackageName()); + } + } + + return false; + } + + /** * Returns true if Wifi scan operation is allowed for this caller * and package. */ @@ -193,19 +329,24 @@ 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; + final long token = Binder.clearCallingIdentity(); + try { + 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; + } finally { + Binder.restoreCallingIdentity(token); } - return false; } /** @@ -237,20 +378,6 @@ public class WifiPermissionsUtil { return pkgName.equals(mWifiPermissionsWrapper.getTopPkgName()); } - /** - * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION - * and a corresponding app op is allowed for this package and uid. - */ - private boolean checkCallersLocationPermission(String pkgName, int uid) { - // Coarse Permission implies Fine permission - if ((mWifiPermissionsWrapper.getUidPermission( - Manifest.permission.ACCESS_COARSE_LOCATION, uid) - == PackageManager.PERMISSION_GRANTED) - && checkAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid)) { - return true; - } - return false; - } private boolean isLocationModeEnabled(String pkgName) { // Location mode check on applications that are later than version. return (mSettingsStore.getLocationModeSetting(mContext) diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java index 853136b90..f4c3ab1af 100644 --- a/service/java/com/android/server/wifi/util/XmlUtil.java +++ b/service/java/com/android/server/wifi/util/XmlUtil.java @@ -332,6 +332,7 @@ public class XmlUtil { public static final String XML_TAG_NO_INTERNET_ACCESS_EXPECTED = "NoInternetAccessExpected"; public static final String XML_TAG_USER_APPROVED = "UserApproved"; public static final String XML_TAG_METERED_HINT = "MeteredHint"; + public static final String XML_TAG_METERED_OVERRIDE = "MeteredOverride"; public static final String XML_TAG_USE_EXTERNAL_SCORES = "UseExternalScores"; public static final String XML_TAG_NUM_ASSOCIATION = "NumAssociation"; public static final String XML_TAG_CREATOR_UID = "CreatorUid"; @@ -445,6 +446,7 @@ public class XmlUtil { configuration.noInternetAccessExpected); XmlUtil.writeNextValue(out, XML_TAG_USER_APPROVED, configuration.userApproved); XmlUtil.writeNextValue(out, XML_TAG_METERED_HINT, configuration.meteredHint); + XmlUtil.writeNextValue(out, XML_TAG_METERED_OVERRIDE, configuration.meteredOverride); XmlUtil.writeNextValue( out, XML_TAG_USE_EXTERNAL_SCORES, configuration.useExternalScores); XmlUtil.writeNextValue(out, XML_TAG_NUM_ASSOCIATION, configuration.numAssociation); @@ -591,6 +593,9 @@ public class XmlUtil { case XML_TAG_METERED_HINT: configuration.meteredHint = (boolean) value; break; + case XML_TAG_METERED_OVERRIDE: + configuration.meteredOverride = (int) value; + break; case XML_TAG_USE_EXTERNAL_SCORES: configuration.useExternalScores = (boolean) value; break; diff --git a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java new file mode 100644 index 000000000..f8d2e5202 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.wifi.EAPConstants; +import android.net.wifi.WifiEnterpriseConfig; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Base64; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; + +/** + * Unit tests for {@link com.android.server.wifi.CarrierNetworkConfig}. + */ +@SmallTest +public class CarrierNetworkConfigTest { + 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; + private static final int TEST_SUBSCRIPTION_ID = 1; + private static final String TEST_CARRIER_NAME = "Test Carrier"; + private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO = + new SubscriptionInfo(TEST_SUBSCRIPTION_ID, null, 0, TEST_CARRIER_NAME, null, 0, 0, + null, 0, null, 0, 0, null); + + @Mock Context mContext; + @Mock CarrierConfigManager mCarrierConfigManager; + @Mock SubscriptionManager mSubscriptionManager; + BroadcastReceiver mBroadcastReceiver; + CarrierNetworkConfig mCarrierNetworkConfig; + + /** + * Generate and return a carrier config for testing + * + * @param ssid The SSID of the carrier network + * @param eapType The EAP type of the carrier network + * @return {@link PersistableBundle} containing carrier config + */ + private PersistableBundle generateTestConfig(String ssid, int eapType) { + PersistableBundle bundle = new PersistableBundle(); + String networkConfig = + new String(Base64.encode(ssid.getBytes(), Base64.DEFAULT)) + "," + eapType; + bundle.putStringArray(CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY, + new String[] {networkConfig}); + return bundle; + } + + /** + * Method to initialize mocks for tests. + */ + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) + .thenReturn(mCarrierConfigManager); + when(mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)) + .thenReturn(mSubscriptionManager); + 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})); + mCarrierNetworkConfig = new CarrierNetworkConfig(mContext); + ArgumentCaptor<BroadcastReceiver> receiver = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(receiver.capture(), any(IntentFilter.class)); + mBroadcastReceiver = receiver.getValue(); + reset(mCarrierConfigManager); + } + + /** + * Verify that {@link CarrierNetworkConfig#isCarrierNetwork} will return true and + * {@link CarrierNetworkConfig#getNetworkEapType} will return the corresponding EAP type + * when the given SSID is associated with a carrier network. + * + * @throws Exception + */ + @Test + public void getExistingCarrierNetworkInfo() throws Exception { + assertTrue(mCarrierNetworkConfig.isCarrierNetwork(TEST_SSID)); + assertEquals(TEST_INTERNAL_EAP_TYPE, mCarrierNetworkConfig.getNetworkEapType(TEST_SSID)); + assertEquals(TEST_CARRIER_NAME, mCarrierNetworkConfig.getCarrierName(TEST_SSID)); + } + + /** + * Verify that {@link CarrierNetworkConfig#isCarrierNetwork} will return false and + * {@link CarrierNetworkConfig#getNetworkEapType} will return -1 when the given SSID is not + * associated with any carrier network. + * + * @throws Exception + */ + @Test + public void getNonCarrierNetworkInfo() throws Exception { + String dummySsid = "Dummy SSID"; + assertFalse(mCarrierNetworkConfig.isCarrierNetwork(dummySsid)); + assertEquals(-1, mCarrierNetworkConfig.getNetworkEapType(dummySsid)); + } + + /** + * Verify that the carrier network config is updated when + * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} intent is received. + * + * @throws Exception + */ + @Test + public void receivedCarrierConfigChangedIntent() throws Exception { + String updatedSsid = "Updated SSID"; + int updatedStandardEapType = EAPConstants.EAP_AKA; + int updatedInternalEapType = WifiEnterpriseConfig.Eap.AKA; + String updatedCarrierName = "Updated Carrier"; + SubscriptionInfo updatedSubscriptionInfo = new SubscriptionInfo(TEST_SUBSCRIPTION_ID, + null, 0, updatedCarrierName, null, 0, 0, null, 0, null, 0, 0, null); + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(Arrays.asList(new SubscriptionInfo[] {updatedSubscriptionInfo})); + when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID)) + .thenReturn(generateTestConfig(updatedSsid, updatedStandardEapType)); + mBroadcastReceiver.onReceive(mContext, + new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + + // Verify that original SSID no longer associated with a carrier network. + assertFalse(mCarrierNetworkConfig.isCarrierNetwork(TEST_SSID)); + assertEquals(-1, mCarrierNetworkConfig.getNetworkEapType(TEST_SSID)); + assertEquals(null, mCarrierNetworkConfig.getCarrierName(TEST_SSID)); + + // Verify that updated SSID is associated with a carrier network. + assertTrue(mCarrierNetworkConfig.isCarrierNetwork(updatedSsid)); + assertEquals(updatedInternalEapType, mCarrierNetworkConfig.getNetworkEapType(updatedSsid)); + assertEquals(updatedCarrierName, mCarrierNetworkConfig.getCarrierName(updatedSsid)); + } + + /** + * Verify that the carrier network config is not updated when non + * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} intent is received. + * + * @throws Exception + */ + @Test + public void receivedNonCarrierConfigChangedIntent() throws Exception { + mBroadcastReceiver.onReceive(mContext, new Intent("dummyIntent")); + verify(mCarrierConfigManager, never()).getConfig(); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java index 01257c16e..19a92b8e4 100644 --- a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java +++ b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java @@ -16,9 +16,13 @@ package com.android.server.wifi; +import static android.os.Process.SYSTEM_UID; + import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import android.content.Context; +import android.content.pm.PackageManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.test.suitebuilder.annotation.SmallTest; @@ -29,6 +33,8 @@ import com.android.server.wifi.util.XmlUtilTest; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -49,6 +55,7 @@ public class NetworkListStoreDataTest { private static final String TEST_SSID = "WifiConfigStoreDataSSID_"; private static final String TEST_CONNECT_CHOICE = "XmlUtilConnectChoice"; private static final long TEST_CONNECT_CHOICE_TIMESTAMP = 0x4566; + private static final String TEST_CREATOR_NAME = "CreatorName"; private static final String SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT = "<Network>\n" + "<WifiConfiguration>\n" @@ -75,10 +82,11 @@ public class NetworkListStoreDataTest { + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" + "<int name=\"UserApproved\" value=\"0\" />\n" + "<boolean name=\"MeteredHint\" value=\"false\" />\n" + + "<int name=\"MeteredOverride\" value=\"0\" />\n" + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" + "<int name=\"NumAssociation\" value=\"0\" />\n" + "<int name=\"CreatorUid\" value=\"%d\" />\n" - + "<null name=\"CreatorName\" />\n" + + "<string name=\"CreatorName\">%s</string>\n" + "<null name=\"CreationTime\" />\n" + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" + "<null name=\"LastUpdateName\" />\n" @@ -125,10 +133,11 @@ public class NetworkListStoreDataTest { + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" + "<int name=\"UserApproved\" value=\"0\" />\n" + "<boolean name=\"MeteredHint\" value=\"false\" />\n" + + "<int name=\"MeteredOverride\" value=\"0\" />\n" + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" + "<int name=\"NumAssociation\" value=\"0\" />\n" + "<int name=\"CreatorUid\" value=\"%d\" />\n" - + "<null name=\"CreatorName\" />\n" + + "<string name=\"CreatorName\">%s</string>\n" + "<null name=\"CreationTime\" />\n" + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" + "<null name=\"LastUpdateName\" />\n" @@ -168,10 +177,15 @@ public class NetworkListStoreDataTest { + "</Network>\n"; private NetworkListStoreData mNetworkListStoreData; + @Mock private Context mContext; + @Mock private PackageManager mPackageManager; @Before public void setUp() throws Exception { - mNetworkListStoreData = new NetworkListStoreData(); + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME); + mNetworkListStoreData = new NetworkListStoreData(mContext); } /** @@ -219,11 +233,13 @@ public class NetworkListStoreDataTest { */ private List<WifiConfiguration> getTestNetworksConfig(boolean shared) { WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.creatorName = TEST_CREATOR_NAME; openNetwork.shared = shared; openNetwork.setIpConfiguration( WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork(); eapNetwork.shared = shared; + eapNetwork.creatorName = TEST_CREATOR_NAME; eapNetwork.setIpConfiguration( WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); List<WifiConfiguration> networkList = new ArrayList<>(); @@ -245,11 +261,11 @@ public class NetworkListStoreDataTest { String openNetworkXml = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, openNetwork.configKey().replaceAll("\"", """), openNetwork.SSID.replaceAll("\"", """), - openNetwork.shared, openNetwork.creatorUid); + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName); String eapNetworkXml = String.format(SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT, eapNetwork.configKey().replaceAll("\"", """), eapNetwork.SSID.replaceAll("\"", """), - eapNetwork.shared, eapNetwork.creatorUid); + eapNetwork.shared, eapNetwork.creatorUid, openNetwork.creatorName); return (openNetworkXml + eapNetworkXml).getBytes(StandardCharsets.UTF_8); } @@ -418,7 +434,8 @@ public class NetworkListStoreDataTest { byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, "InvalidConfigKey", openNetwork.SSID.replaceAll("\"", """), - openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8); + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName) + .getBytes(StandardCharsets.UTF_8); deserializeData(xmlData, true); } @@ -446,4 +463,96 @@ public class NetworkListStoreDataTest { assertNotEquals(eapNetwork.SSID, network.SSID); } } + + /** + * Verify that a saved network config with invalid creatorUid resets it to + * {@link android.os.Process#SYSTEM_UID}. + */ + public void parseNetworkWithInvalidCreatorUidResetsToSystem() throws Exception { + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.creatorUid = -1; + // Return null for invalid uid. + when(mPackageManager.getNameForUid(eq(openNetwork.creatorUid))).thenReturn(null); + + byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName) + .getBytes(StandardCharsets.UTF_8); + List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true); + assertEquals(1, deserializedNetworks.size()); + assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey()); + assertEquals(SYSTEM_UID, deserializedNetworks.get(0).creatorUid); + assertEquals(TEST_CREATOR_NAME, deserializedNetworks.get(0).creatorName); + } + + /** + * Verify that a saved network config with invalid creatorName resets it to the package name + * provided {@link PackageManager} for the creatorUid. + */ + public void parseNetworkWithInvalidCreatorNameResetsToPackageNameForCreatorUid() + throws Exception { + String badCreatorName = "bad"; + String correctCreatorName = "correct"; + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.creatorUid = 1324422; + openNetwork.creatorName = badCreatorName; + when(mPackageManager.getNameForUid(eq(openNetwork.creatorUid))) + .thenReturn(correctCreatorName); + + byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName) + .getBytes(StandardCharsets.UTF_8); + List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true); + assertEquals(1, deserializedNetworks.size()); + assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey()); + assertEquals(openNetwork.creatorUid, deserializedNetworks.get(0).creatorUid); + assertEquals(correctCreatorName, deserializedNetworks.get(0).creatorName); + } + + /** + * Verify that a saved network config with invalid creatorName resets it to the package name + * provided {@link PackageManager} for the creatorUid. + */ + public void parseNetworkWithNullCreatorNameResetsToPackageNameForCreatorUid() + throws Exception { + String correctCreatorName = "correct"; + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.creatorUid = 1324422; + openNetwork.creatorName = null; + when(mPackageManager.getNameForUid(eq(openNetwork.creatorUid))) + .thenReturn(correctCreatorName); + + byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName) + .getBytes(StandardCharsets.UTF_8); + List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true); + assertEquals(1, deserializedNetworks.size()); + assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey()); + assertEquals(openNetwork.creatorUid, deserializedNetworks.get(0).creatorUid); + assertEquals(correctCreatorName, deserializedNetworks.get(0).creatorName); + } + + /** + * Verify that a saved network config with valid creatorUid is preserved. + */ + public void parseNetworkWithValidCreatorUid() throws Exception { + WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.creatorUid = 1324422; + + byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT, + openNetwork.configKey().replaceAll("\"", """), + openNetwork.SSID.replaceAll("\"", """), + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName) + .getBytes(StandardCharsets.UTF_8); + List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true); + assertEquals(1, deserializedNetworks.size()); + assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey()); + assertEquals(openNetwork.creatorUid, deserializedNetworks.get(0).creatorUid); + assertEquals(TEST_CREATOR_NAME, deserializedNetworks.get(0).creatorName); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java new file mode 100644 index 000000000..5a0b011f2 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java @@ -0,0 +1,702 @@ +/* + * 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.OpenNetworkNotifier.DEFAULT_REPEAT_DELAY_SEC; + +import static org.junit.Assert.assertEquals; +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 android.util.ArraySet; + +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; +import java.util.Set; + +/** + * Unit tests for {@link OpenNetworkNotifier}. + */ +public class OpenNetworkNotifierTest { + + 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; + + @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 WifiStateMachine mWifiStateMachine; + @Mock private OpenNetworkRecommender mOpenNetworkRecommender; + @Mock private ConnectToNetworkNotificationBuilder mNotificationBuilder; + @Mock private UserManager mUserManager; + private OpenNetworkNotifier mNotificationController; + private TestLooper mLooper; + private BroadcastReceiver mBroadcastReceiver; + private ContentObserver mContentObserver; + private ScanResult mDummyNetwork; + private List<ScanDetail> mOpenNetworks; + private Set<String> mBlacklistedSsids; + + + /** 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_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; + when(mOpenNetworkRecommender.recommendNetwork(any(), any())).thenReturn(mDummyNetwork); + mOpenNetworks = new ArrayList<>(); + mOpenNetworks.add(new ScanDetail(mDummyNetwork, null /* networkDetail */)); + mBlacklistedSsids = new ArraySet<>(); + + mLooper = new TestLooper(); + mNotificationController = new OpenNetworkNotifier( + mContext, mLooper.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, + mWifiConfigManager, mWifiConfigStore, mWifiStateMachine, mOpenNetworkRecommender, + 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); + } + + /** + * On {@link OpenNetworkNotifier} construction, WifiMetrics should track setting state. + */ + @Test + public void onCreate_setWifiNetworksAvailableNotificationSettingState() { + verify(mWifiMetrics).setIsWifiNetworksAvailableNotificationEnabled(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_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(0); + mContentObserver.onChange(false); + + verify(mWifiMetrics).setIsWifiNetworksAvailableNotificationEnabled(false); + } + + /** + * When scan results with open networks are handled, a notification is posted. + */ + @Test + public void handleScanResults_hasOpenNetworks_notificationDisplayed() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + } + + /** + * When scan results with no open networks are handled, a notification is not posted. + */ + @Test + public void handleScanResults_emptyList_notificationNotDisplayed() { + mNotificationController.handleScanResults(new ArrayList<>()); + + verify(mOpenNetworkRecommender, never()).recommendNetwork(any(), any()); + 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_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(0); + mContentObserver.onChange(false); + mNotificationController.handleScanResults(new ArrayList<>()); + + verify(mOpenNetworkRecommender, never()).recommendNetwork(any(), any()); + verify(mNotificationManager, never()).notify(anyInt(), any()); + } + + /** + * When a notification is showing and scan results with no open networks are handled, the + * notification is cleared. + */ + @Test + public void handleScanResults_notificationShown_emptyList_notificationCleared() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + 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(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + when(mOpenNetworkRecommender.recommendNetwork(any(), any())).thenReturn(null); + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mNotificationManager).cancel(anyInt()); + } + + /** + * When a notification is showing, screen is off, and scan results with no open networks are + * handled, the notification is cleared. + */ + @Test + public void handleScanResults_notificationShown_screenOff_emptyList_notificationCleared() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mNotificationController.handleScreenStateChanged(false); + mNotificationController.handleScanResults(new ArrayList<>()); + + verify(mNotificationManager).cancel(anyInt()); + } + + /** + * When {@link OpenNetworkNotifier#clearPendingNotification(boolean)} is called and a + * notification is shown, clear the notification. + */ + @Test + public void clearPendingNotification_clearsNotificationIfOneIsShowing() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mNotificationController.clearPendingNotification(true); + + verify(mNotificationManager).cancel(anyInt()); + } + + /** + * When {@link OpenNetworkNotifier#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 open networks. + */ + @Test + public void screenOff_notificationNotShowing_handleScanResults_notificationNotDisplayed() { + mNotificationController.handleScreenStateChanged(false); + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender, never()).recommendNetwork(any(), any()); + 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(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + 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; + mOpenNetworks.add(new ScanDetail(newNetwork, null /* networkDetail */)); + when(mOpenNetworkRecommender.recommendNetwork(any(), any())).thenReturn(newNetwork); + + mNotificationController.handleScreenStateChanged(false); + mNotificationController.handleScanResults(mOpenNetworks); + + // Recommendation changed + verify(mOpenNetworkRecommender, times(2)).recommendNetwork( + mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(newNetwork); + verify(mWifiMetrics).incrementNumOpenNetworkRecommendationUpdates(); + verify(mNotificationManager, times(2)).notify(anyInt(), any()); + } + + /** + * When a notification is posted and cleared without resetting delay, the next scan with open + * networks should not post another notification. + */ + @Test + public void postNotification_clearNotificationWithoutDelayReset_shouldNotPostNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mNotificationController.clearPendingNotification(false); + + verify(mNotificationManager).cancel(anyInt()); + + mNotificationController.handleScanResults(mOpenNetworks); + + // no new notification posted + verify(mNotificationManager).notify(anyInt(), any()); + } + + /** + * When a notification is posted and cleared without resetting delay, the next scan with open + * networks should post a notification. + */ + @Test + public void postNotification_clearNotificationWithDelayReset_shouldPostNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mNotificationController.clearPendingNotification(true); + + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender, times(2)).recommendNetwork( + mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder, times(2)).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics, times(2)).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager, times(2)).notify(anyInt(), any()); + } + + /** + * When user dismissed notification and there is a recommended network, network ssid should be + * blacklisted. + */ + @Test + public void userDismissedNotification_shouldBlacklistNetwork() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive( + mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_USER_DISMISSED_NOTIFICATION)); + + verify(mWifiConfigManager).saveToStore(false /* forceWrite */); + + mNotificationController.clearPendingNotification(true); + List<ScanDetail> scanResults = mOpenNetworks; + mNotificationController.handleScanResults(scanResults); + + Set<String> expectedBlacklist = new ArraySet<>(); + expectedBlacklist.add(mDummyNetwork.SSID); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, expectedBlacklist); + verify(mWifiMetrics).setOpenNetworkRecommenderBlacklistSize(expectedBlacklist.size()); + } + + /** + * When a notification is posted and cleared without resetting delay, after the delay has passed + * the next scan with open networks should post a notification. + */ + @Test + public void delaySet_delayPassed_shouldPostNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + 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(mOpenNetworks); + + verify(mOpenNetworkRecommender, times(2)).recommendNetwork( + mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder, times(2)).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics, times(2)).incrementConnectToNetworkNotification( + 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(mOpenNetworks); + + verify(mOpenNetworkRecommender, never()).recommendNetwork(any(), any()); + verify(mNotificationManager, never()).notify(anyInt(), any()); + } + + /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} clears the showing notification. */ + @Test + public void userHasDisallowConfigWifiRestriction_showingNotificationIsCleared() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) + .thenReturn(true); + + mNotificationController.handleScanResults(mOpenNetworks); + + 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, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + verify(mWifiStateMachine, 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(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + verify(mWifiStateMachine).sendMessage(any(Message.class)); + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); + verify(mWifiMetrics).incrementConnectToNetworkNotificationAction( + 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(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.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( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK, + ConnectToNetworkNotificationAndActionCount.ACTION_PICK_WIFI_NETWORK); + } + + /** + * {@link OpenNetworkNotifier#handleWifiConnected()} does not post connected notification if + * the connecting notification is not showing + */ + @Test + public void networkConnectionSuccess_wasNotInConnectingFlow_doesNothing() { + mNotificationController.handleWifiConnected(); + + verify(mNotificationManager, never()).notify(anyInt(), any()); + verify(mWifiMetrics, never()).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK); + } + + /** + * {@link OpenNetworkNotifier#handleWifiConnected()} clears notification that is not connecting. + */ + @Test + public void networkConnectionSuccess_wasShowingNotification_clearsNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mNotificationController.handleWifiConnected(); + + verify(mNotificationManager).cancel(anyInt()); + } + + /** + * {@link OpenNetworkNotifier#handleWifiConnected()} posts the connected notification if + * the connecting notification is showing. + */ + @Test + public void networkConnectionSuccess_wasInConnectingFlow_postsConnectedNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); + verify(mWifiMetrics).incrementConnectToNetworkNotificationAction( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK, + ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); + verify(mNotificationManager, times(2)).notify(anyInt(), any()); + + mNotificationController.handleWifiConnected(); + + // Connected Notification + verify(mNotificationBuilder).createNetworkConnectedNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK); + verify(mNotificationManager, times(3)).notify(anyInt(), any()); + } + + /** + * {@link OpenNetworkNotifier#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( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT); + } + + /** + * {@link OpenNetworkNotifier#handleConnectionFailure()} posts the Failed to Connect + * notification if the connecting notification is showing. + */ + @Test + public void networkConnectionFailure_wasInConnectingFlow_postsFailedToConnectNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); + verify(mWifiMetrics).incrementConnectToNetworkNotificationAction( + 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(); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + 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 WifiStateMachine#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(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + ArgumentCaptor<Message> connectMessageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mWifiStateMachine).sendMessage(connectMessageCaptor.capture()); + Message connectMessage = connectMessageCaptor.getValue(); + + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); + verify(mWifiMetrics).incrementConnectToNetworkNotificationAction( + 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(); + verify(mWifiMetrics).incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT); + verify(mWifiMetrics).incrementNumOpenNetworkConnectMessageFailedToSend(); + verify(mNotificationManager, times(3)).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder + .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( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT, + ConnectToNetworkNotificationAndActionCount + .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/OpenNetworkRecommenderTest.java b/tests/wifitests/src/com/android/server/wifi/OpenNetworkRecommenderTest.java new file mode 100644 index 000000000..720ec3797 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/OpenNetworkRecommenderTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.net.wifi.ScanResult; +import android.util.ArraySet; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Tests for {@link OpenNetworkRecommender}. + */ +public class OpenNetworkRecommenderTest { + + 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 OpenNetworkRecommender mOpenNetworkRecommender; + private Set<String> mBlacklistedSsids; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mOpenNetworkRecommender = new OpenNetworkRecommender(); + mBlacklistedSsids = new ArraySet<>(); + } + + private List<ScanDetail> createOpenScanResults(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 open networks contain only one network, that network should be returned. */ + @Test + public void onlyNetworkIsRecommended() { + List<ScanDetail> scanResults = createOpenScanResults(TEST_SSID_1); + scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL; + + ScanResult actual = mOpenNetworkRecommender.recommendNetwork( + scanResults, mBlacklistedSsids); + 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 = createOpenScanResults(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 = mOpenNetworkRecommender.recommendNetwork( + scanResults, mBlacklistedSsids); + ScanResult expected = scanResults.get(1).getScanResult(); + assertEquals(expected, actual); + } + + /** + * If the best available open network is blacklisted, no networks should be recommended. + */ + @Test + public void blacklistBestNetworkSsid_shouldNeverRecommendNetwork() { + List<ScanDetail> scanResults = createOpenScanResults(TEST_SSID_1, TEST_SSID_2); + scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL + 1; + scanResults.get(1).getScanResult().level = MIN_RSSI_LEVEL; + mBlacklistedSsids.add(TEST_SSID_1); + + ScanResult actual = mOpenNetworkRecommender.recommendNetwork( + scanResults, mBlacklistedSsids); + assertNull(actual); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java b/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java index 689ade80a..7300cb256 100644 --- a/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java @@ -41,6 +41,7 @@ import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.util.test.BidirectionalAsyncChannel; +import com.android.server.wifi.util.WifiPermissionsUtil; import org.junit.After; import org.junit.Before; @@ -69,6 +70,8 @@ public class RttServiceTest { WifiInjector mWifiInjector; @Mock IWificond mWificond; + @Mock + WifiPermissionsUtil mWifiPermissionsUtil; RttService.RttServiceImpl mRttServiceImpl; ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = ArgumentCaptor @@ -80,6 +83,9 @@ public class RttServiceTest { mLooper = new TestLooper(); when(mWifiInjector.makeWificond()).thenReturn(mWificond); when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative); + when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil); + when(mWifiPermissionsUtil.checkCallersLocationPermission(any(), anyInt())) + .thenReturn(true); mRttServiceImpl = new RttService.RttServiceImpl(mContext, mLooper.getLooper(), mWifiInjector); mRttServiceImpl.startService(); @@ -100,7 +106,7 @@ public class RttServiceTest { // Create and connect a bi-directional async channel. private BidirectionalAsyncChannel connectChannel(Handler handler) { BidirectionalAsyncChannel channel = new BidirectionalAsyncChannel(); - channel.connect(mLooper.getLooper(), mRttServiceImpl.getMessenger(), + channel.connect(mLooper.getLooper(), mRttServiceImpl.getMessenger(null, new int[1]), handler); mLooper.dispatchAll(); channel.assertConnected(); @@ -271,6 +277,22 @@ public class RttServiceTest { } /** + * Test RTT fails without proper location permission + */ + @Test + public void testEnableResponderFailureNoPermission() throws Exception { + when(mWifiPermissionsUtil.checkCallersLocationPermission(any(), anyInt())) + .thenReturn(false); + startWifi(); + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel channel = connectChannel(handler); + Message message = sendEnableResponder(channel, handler, CLIENT_KEY1, null); + // RTT operations failed without proper permission. + assertEquals("expected permission denied, but got " + message.what, + RttManager.REASON_PERMISSION_DENIED, message.arg1); + } + + /** * Test RTT ranging with empty RttParams. */ @Test diff --git a/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java new file mode 100644 index 000000000..606b825ee --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Unit tests for {@link com.android.server.wifi.SsidSetStoreData}. + */ +public class SsidSetStoreDataTest { + private static final String TEST_NOTIFIER_NAME = "TestNetwork"; + private static final String TEST_SSID1 = "SSID 1"; + private static final String TEST_SSID2 = "SSID 2"; + private static final String TEST_SSID_SET_XML_STRING = + "<set name=\"SSIDSet\">\n" + + "<string>" + TEST_SSID1 + "</string>\n" + + "<string>" + TEST_SSID2 + "</string>\n" + + "</set>\n"; + private static final byte[] TEST_SSID_SET_XML_BYTES = + TEST_SSID_SET_XML_STRING.getBytes(StandardCharsets.UTF_8); + + @Mock SsidSetStoreData.DataSource mDataSource; + SsidSetStoreData mSsidSetStoreData; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mSsidSetStoreData = new SsidSetStoreData(TEST_NOTIFIER_NAME, mDataSource); + } + + /** + * Helper function for serializing configuration data to a XML block. + * + * @param shared Flag indicating serializing shared or user configurations + * @return byte[] of the XML data + * @throws Exception + */ + private byte[] serializeData(boolean shared) throws Exception { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + mSsidSetStoreData.serializeData(out, shared); + out.flush(); + return outputStream.toByteArray(); + } + + /** + * Helper function for parsing configuration data from a XML block. + * + * @param data XML data to parse from + * @param shared Flag indicating parsing of shared or user configurations + * @throws Exception + */ + private void deserializeData(byte[] data, boolean shared) throws Exception { + final XmlPullParser in = Xml.newPullParser(); + final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + in.setInput(inputStream, StandardCharsets.UTF_8.name()); + mSsidSetStoreData.deserializeData(in, in.getDepth(), shared); + } + + /** + * Verify that a XmlPullParserException will be thrown when attempting to serialize data + * to the share store. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void serializeShareData() throws Exception { + serializeData(true /* shared */); + } + + /** + * Verify that a XmlPullParserException will be thrown when attempting to deserialize + * data from the share store. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void deserializeShareData() throws Exception { + deserializeData(new byte[0], true /* shared */); + } + + /** + * Verify that serializing the user store data without any configuration doesn't cause any + * crash and no data should be serialized. + * + * @throws Exception + */ + @Test + public void serializeEmptyConfigs() throws Exception { + when(mDataSource.getSsids()).thenReturn(new HashSet<String>()); + assertEquals(0, serializeData(false /* shared */).length); + } + + /** + * Verify that parsing an empty data doesn't cause any crash and no configuration should + * be deserialized. + * + * @throws Exception + */ + @Test + public void deserializeEmptyStoreData() throws Exception { + deserializeData(new byte[0], false /* shared */); + verify(mDataSource, never()).setSsids(any(Set.class)); + } + + /** + * Verify that {@link SsidSetStoreData} does not support share data. + * + * @throws Exception + */ + @Test + public void supportShareData() throws Exception { + assertFalse(mSsidSetStoreData.supportShareData()); + } + + /** + * Verify that the store data is serialized correctly, matches the predefined test XML data. + * + * @throws Exception + */ + @Test + public void serializeSsidSet() throws Exception { + Set<String> ssidSet = new HashSet<>(); + ssidSet.add(TEST_SSID1); + ssidSet.add(TEST_SSID2); + when(mDataSource.getSsids()).thenReturn(ssidSet); + byte[] actualData = serializeData(false /* shared */); + assertTrue(Arrays.equals(TEST_SSID_SET_XML_BYTES, actualData)); + } + + /** + * Verify that the store data is deserialized correctly using the predefined test XML data. + * + * @throws Exception + */ + @Test + public void deserializeSsidSet() throws Exception { + Set<String> ssidSet = new HashSet<>(); + ssidSet.add(TEST_SSID1); + ssidSet.add(TEST_SSID2); + deserializeData(TEST_SSID_SET_XML_BYTES, false /* shared */); + verify(mDataSource).setSsids(eq(ssidSet)); + } + + /** + * Verify that a XmlPullParserException will be thrown when parsing a SSIDSet set with an + * unknown tag. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void parseSetWithUnknownTag() throws Exception { + String ssidSet = + "<set name=\"SSIDSet\">\n" + + "<string>" + TEST_SSID1 + "</string>\n" + + "<string>" + TEST_SSID2 + "</string>\n" + + "<Unknown>" + "badInput" + "</Unknown>" // Unknown tag. + + "</set>\n"; + deserializeData(ssidSet.getBytes(StandardCharsets.UTF_8), false /* shared */); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java index 02064d87e..a923475f1 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java @@ -268,7 +268,9 @@ public class WifiApConfigStoreTest { assertFalse(WifiApConfigStore.validateApWifiConfiguration(config)); // now check a valid SSID with a random length - config.SSID = generateRandomString(mRandom.nextInt(WifiApConfigStore.SSID_MAX_LEN + 1)); + int validLength = WifiApConfigStore.SSID_MAX_LEN - WifiApConfigStore.SSID_MIN_LEN; + config.SSID = generateRandomString( + mRandom.nextInt(validLength) + WifiApConfigStore.SSID_MIN_LEN); assertTrue(WifiApConfigStore.validateApWifiConfiguration(config)); } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java index 7509c18b3..0fa660081 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java @@ -43,7 +43,6 @@ import android.text.TextUtils; import android.util.Pair; import com.android.internal.R; -import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; @@ -101,7 +100,6 @@ public class WifiConfigManagerTest { @Mock private TelephonyManager mTelephonyManager; @Mock private WifiKeyStore mWifiKeyStore; @Mock private WifiConfigStore mWifiConfigStore; - @Mock private WifiConfigStoreLegacy mWifiConfigStoreLegacy; @Mock private PackageManager mPackageManager; @Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal; @Mock private WifiPermissionsUtil mWifiPermissionsUtil; @@ -1144,7 +1142,7 @@ public class WifiConfigManagerTest { ScanDetailCache retrievedScanDetailCache = mWifiConfigManager.getScanDetailCacheForNetwork(result.getNetworkId()); assertEquals(1, retrievedScanDetailCache.size()); - ScanResult retrievedScanResult = retrievedScanDetailCache.get(scanResult.BSSID); + ScanResult retrievedScanResult = retrievedScanDetailCache.getScanResult(scanResult.BSSID); ScanTestUtil.assertScanResultEquals(scanResult, retrievedScanResult); } @@ -1521,6 +1519,45 @@ public class WifiConfigManagerTest { } /** + * Verifies that the list of PNO networks does not contain any permanently or temporarily + * disabled networks. + * {@link WifiConfigManager#retrievePnoNetworkList()}. + */ + @Test + public void testRetrievePnoListDoesNotContainDisabledNetworks() throws Exception { + // Create and add 2 networks. + WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(); + WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork(); + + NetworkUpdateResult result1 = verifyAddNetworkToWifiConfigManager(network1); + NetworkUpdateResult result2 = verifyAddNetworkToWifiConfigManager(network2); + + // Enable all of them. + verifyUpdateNetworkSelectionStatus( + result1.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0); + verifyUpdateNetworkSelectionStatus( + result2.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0); + + // Set network1 to temporarily disabled. The threshold for association rejection is 5, so + // disable it 5 times to actually mark it temporarily disabled. + int assocRejectReason = NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION; + int assocRejectThreshold = + WifiConfigManager.NETWORK_SELECTION_DISABLE_THRESHOLD[assocRejectReason]; + for (int i = 1; i <= assocRejectThreshold; i++) { + verifyUpdateNetworkSelectionStatus(result1.getNetworkId(), assocRejectReason, i); + } + + // Set network 2 to permanently disabled. + verifyUpdateNetworkSelectionStatus( + result2.getNetworkId(), NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER, 0); + + // Retrieve the Pno network list & verify both networks are not included. + List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks = + mWifiConfigManager.retrievePnoNetworkList(); + assertEquals(0, pnoNetworks.size()); + } + + /** * Verifies the linking of networks when they have the same default GW Mac address in * {@link WifiConfigManager#getOrCreateScanDetailCacheForNetwork(WifiConfiguration)}. */ @@ -2369,7 +2406,7 @@ public class WifiConfigManagerTest { } /** - * Verifies that the foreground user stop using {@link WifiConfigManager#handleUserStop(int)} + * Verifies that the user stop handling using {@link WifiConfigManager#handleUserStop(int)} * and ensures that the store is written only when the foreground user is stopped. */ @Test @@ -2392,6 +2429,49 @@ public class WifiConfigManagerTest { } /** + * Verifies that the user stop handling using {@link WifiConfigManager#handleUserStop(int)} + * and ensures that the shared data is not lost when the foreground user is stopped. + */ + @Test + public void testHandleUserStopDoesNotClearSharedData() throws Exception { + int user1 = TEST_DEFAULT_USER; + + // + // Setup the database for the user before initiating stop. + // + int appId = 674; + // Create 2 networks. 1 for user1, and 1 shared. + final WifiConfiguration user1Network = WifiConfigurationTestUtil.createPskNetwork(); + user1Network.shared = false; + user1Network.creatorUid = UserHandle.getUid(user1, appId); + final WifiConfiguration sharedNetwork = WifiConfigurationTestUtil.createPskNetwork(); + + // Set up the store data that is loaded initially. + List<WifiConfiguration> sharedNetworks = new ArrayList<WifiConfiguration>() { + { + add(sharedNetwork); + } + }; + List<WifiConfiguration> user1Networks = new ArrayList<WifiConfiguration>() { + { + add(user1Network); + } + }; + setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>()); + assertTrue(mWifiConfigManager.loadFromStore()); + verify(mWifiConfigStore).read(); + + // Ensure that we have 2 networks in the database before the stop. + assertEquals(2, mWifiConfigManager.getConfiguredNetworks().size()); + + mWifiConfigManager.handleUserStop(user1); + + // Ensure that we only have 1 shared network in the database after the stop. + assertEquals(1, mWifiConfigManager.getConfiguredNetworks().size()); + assertEquals(sharedNetwork.SSID, mWifiConfigManager.getConfiguredNetworks().get(0).SSID); + } + + /** * Verifies the foreground user unlock via {@link WifiConfigManager#handleUserUnlock(int)} * results in a store read after bootup. */ @@ -2539,55 +2619,6 @@ public class WifiConfigManagerTest { } /** - * Verifies the loading of networks using {@link WifiConfigManager#migrateFromLegacyStore()} ()} - * attempts to migrate data from legacy stores when the legacy store files are present. - */ - @Test - public void testMigrationFromLegacyStore() throws Exception { - // Create the store data to be returned from legacy stores. - List<WifiConfiguration> networks = new ArrayList<>(); - networks.add(WifiConfigurationTestUtil.createPskNetwork()); - networks.add(WifiConfigurationTestUtil.createEapNetwork()); - networks.add(WifiConfigurationTestUtil.createWepNetwork()); - String deletedEphemeralSSID = "EphemeralSSID"; - Set<String> deletedEphermalSSIDs = new HashSet<>(Arrays.asList(deletedEphemeralSSID)); - WifiConfigStoreDataLegacy storeData = - new WifiConfigStoreDataLegacy(networks, deletedEphermalSSIDs); - - when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(true); - when(mWifiConfigStoreLegacy.read()).thenReturn(storeData); - - // Now trigger the migration from legacy store. This should populate the in memory list with - // all the networks above from the legacy store. - assertTrue(mWifiConfigManager.migrateFromLegacyStore()); - - verify(mWifiConfigStoreLegacy).read(); - verify(mWifiConfigStoreLegacy).removeStores(); - - List<WifiConfiguration> retrievedNetworks = - mWifiConfigManager.getConfiguredNetworksWithPasswords(); - WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( - networks, retrievedNetworks); - assertTrue(mWifiConfigManager.wasEphemeralNetworkDeleted(deletedEphemeralSSID)); - } - - /** - * Verifies the loading of networks using {@link WifiConfigManager#migrateFromLegacyStore()} ()} - * does not attempt to migrate data from legacy stores when the legacy store files are absent - * (i.e migration was already done once). - */ - @Test - public void testNoDuplicateMigrationFromLegacyStore() throws Exception { - when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(false); - - // Now trigger a migration from legacy store. - assertTrue(mWifiConfigManager.migrateFromLegacyStore()); - - verify(mWifiConfigStoreLegacy, never()).read(); - verify(mWifiConfigStoreLegacy, never()).removeStores(); - } - - /** * Verifies the loading of networks using {@link WifiConfigManager#loadFromStore()} does * not attempt to read from any of the stores (new or legacy) when the store files are * not present. @@ -2595,12 +2626,10 @@ public class WifiConfigManagerTest { @Test public void testFreshInstallDoesNotLoadFromStore() throws Exception { when(mWifiConfigStore.areStoresPresent()).thenReturn(false); - when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(false); assertTrue(mWifiConfigManager.loadFromStore()); verify(mWifiConfigStore, never()).read(); - verify(mWifiConfigStoreLegacy, never()).read(); assertTrue(mWifiConfigManager.getConfiguredNetworksWithPasswords().isEmpty()); } @@ -2613,11 +2642,9 @@ public class WifiConfigManagerTest { public void testHandleUserSwitchAfterFreshInstall() throws Exception { int user2 = TEST_DEFAULT_USER + 1; when(mWifiConfigStore.areStoresPresent()).thenReturn(false); - when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(false); assertTrue(mWifiConfigManager.loadFromStore()); verify(mWifiConfigStore, never()).read(); - verify(mWifiConfigStoreLegacy, never()).read(); setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>()); // Now switch the user to user 2. @@ -3376,7 +3403,7 @@ public class WifiConfigManagerTest { mWifiConfigManager = new WifiConfigManager( mContext, mClock, mUserManager, mTelephonyManager, - mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, + mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil, mWifiPermissionsWrapper, mNetworkListStoreData, mDeletedEphemeralSsidsStoreData); mWifiConfigManager.enableVerboseLogging(1); @@ -3971,7 +3998,7 @@ public class WifiConfigManagerTest { ScanDetailCache retrievedScanDetailCache = mWifiConfigManager.getScanDetailCacheForNetwork(network.networkId); assertEquals(1, retrievedScanDetailCache.size()); - ScanResult retrievedScanResult = retrievedScanDetailCache.get(scanResult.BSSID); + ScanResult retrievedScanResult = retrievedScanDetailCache.getScanResult(scanResult.BSSID); ScanTestUtil.assertScanResultEquals(scanResult, retrievedScanResult); } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreLegacyTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreLegacyTest.java deleted file mode 100644 index 4b4e875a1..000000000 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreLegacyTest.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wifi; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import android.app.test.MockAnswerUtil.AnswerWithArguments; -import android.net.IpConfiguration; -import android.net.wifi.WifiConfiguration; -import android.test.suitebuilder.annotation.SmallTest; -import android.text.TextUtils; -import android.util.SparseArray; - -import com.android.server.net.IpConfigStore; -import com.android.server.wifi.hotspot2.LegacyPasspointConfig; -import com.android.server.wifi.hotspot2.LegacyPasspointConfigParser; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Unit tests for {@link com.android.server.wifi.WifiConfigStoreLegacy}. - */ -@SmallTest -public class WifiConfigStoreLegacyTest { - private static final String MASKED_FIELD_VALUE = "*"; - - // Test mocks - @Mock private WifiNative mWifiNative; - @Mock private WifiNetworkHistory mWifiNetworkHistory; - @Mock private IpConfigStore mIpconfigStore; - @Mock private LegacyPasspointConfigParser mPasspointConfigParser; - - /** - * Test instance of WifiConfigStore. - */ - private WifiConfigStoreLegacy mWifiConfigStore; - - - /** - * Setup the test environment. - */ - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mWifiConfigStore = new WifiConfigStoreLegacy(mWifiNetworkHistory, mWifiNative, - mIpconfigStore, mPasspointConfigParser); - } - - /** - * Called after each test - */ - @After - public void cleanup() { - validateMockitoUsage(); - } - - /** - * Verify loading of network configurations from legacy stores. This is verifying the population - * of the masked wpa_supplicant fields using wpa_supplicant.conf file. - */ - @Test - public void testLoadFromStores() throws Exception { - WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork(); - WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork(); - WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork(); - WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork(); - eapNetwork.enterpriseConfig.setPassword("EapPassword"); - - // Initialize Passpoint configuration data. - int passpointNetworkId = 1234; - String fqdn = passpointNetwork.FQDN; - String providerFriendlyName = passpointNetwork.providerFriendlyName; - long[] roamingConsortiumIds = new long[] {0x1234, 0x5678}; - String realm = "test.com"; - String imsi = "214321"; - - // Update Passpoint network. - // Network ID is used for lookup network extras, so use an unique ID for passpoint network. - passpointNetwork.networkId = passpointNetworkId; - passpointNetwork.enterpriseConfig.setPassword("PaspointPassword"); - // Reset FQDN and provider friendly name so that the derived network from #read will - // obtained these information from networkExtras and {@link LegacyPasspointConfigParser}. - passpointNetwork.FQDN = null; - passpointNetwork.providerFriendlyName = null; - - final List<WifiConfiguration> networks = new ArrayList<>(); - networks.add(pskNetwork); - networks.add(wepNetwork); - networks.add(eapNetwork); - networks.add(passpointNetwork); - - // Setup legacy Passpoint configuration data. - Map<String, LegacyPasspointConfig> passpointConfigs = new HashMap<>(); - LegacyPasspointConfig passpointConfig = new LegacyPasspointConfig(); - passpointConfig.mFqdn = fqdn; - passpointConfig.mFriendlyName = providerFriendlyName; - passpointConfig.mRoamingConsortiumOis = roamingConsortiumIds; - passpointConfig.mRealm = realm; - passpointConfig.mImsi = imsi; - passpointConfigs.put(fqdn, passpointConfig); - - // Return the config data with passwords masked from wpa_supplicant control interface. - doAnswer(new AnswerWithArguments() { - public boolean answer(Map<String, WifiConfiguration> configs, - SparseArray<Map<String, String>> networkExtras) { - for (Map.Entry<String, WifiConfiguration> entry: - createWpaSupplicantLoadData(networks).entrySet()) { - configs.put(entry.getKey(), entry.getValue()); - } - // Setup networkExtras for Passpoint configuration. - networkExtras.put(passpointNetworkId, createNetworkExtrasForPasspointConfig(fqdn)); - return true; - } - }).when(mWifiNative).migrateNetworksFromSupplicant(any(Map.class), any(SparseArray.class)); - - when(mPasspointConfigParser.parseConfig(anyString())).thenReturn(passpointConfigs); - WifiConfigStoreLegacy.WifiConfigStoreDataLegacy storeData = mWifiConfigStore.read(); - - // Update the expected configuration for Passpoint network. - passpointNetwork.isLegacyPasspointConfig = true; - passpointNetwork.FQDN = fqdn; - passpointNetwork.providerFriendlyName = providerFriendlyName; - passpointNetwork.roamingConsortiumIds = roamingConsortiumIds; - passpointNetwork.enterpriseConfig.setRealm(realm); - passpointNetwork.enterpriseConfig.setPlmn(imsi); - - WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore( - networks, storeData.getConfigurations()); - } - - private SparseArray<IpConfiguration> createIpConfigStoreLoadData( - List<WifiConfiguration> configurations) { - SparseArray<IpConfiguration> newIpConfigurations = new SparseArray<>(); - for (WifiConfiguration config : configurations) { - newIpConfigurations.put( - config.configKey().hashCode(), - new IpConfiguration(config.getIpConfiguration())); - } - return newIpConfigurations; - } - - private Map<String, String> createPskMap(List<WifiConfiguration> configurations) { - Map<String, String> pskMap = new HashMap<>(); - for (WifiConfiguration config : configurations) { - if (!TextUtils.isEmpty(config.preSharedKey)) { - pskMap.put(config.configKey(), config.preSharedKey); - } - } - return pskMap; - } - - private Map<String, String> createWepKey0Map(List<WifiConfiguration> configurations) { - Map<String, String> wepKeyMap = new HashMap<>(); - for (WifiConfiguration config : configurations) { - if (!TextUtils.isEmpty(config.wepKeys[0])) { - wepKeyMap.put(config.configKey(), config.wepKeys[0]); - } - } - return wepKeyMap; - } - - private Map<String, String> createWepKey1Map(List<WifiConfiguration> configurations) { - Map<String, String> wepKeyMap = new HashMap<>(); - for (WifiConfiguration config : configurations) { - if (!TextUtils.isEmpty(config.wepKeys[1])) { - wepKeyMap.put(config.configKey(), config.wepKeys[1]); - } - } - return wepKeyMap; - } - - private Map<String, String> createWepKey2Map(List<WifiConfiguration> configurations) { - Map<String, String> wepKeyMap = new HashMap<>(); - for (WifiConfiguration config : configurations) { - if (!TextUtils.isEmpty(config.wepKeys[2])) { - wepKeyMap.put(config.configKey(), config.wepKeys[2]); - } - } - return wepKeyMap; - } - - private Map<String, String> createWepKey3Map(List<WifiConfiguration> configurations) { - Map<String, String> wepKeyMap = new HashMap<>(); - for (WifiConfiguration config : configurations) { - if (!TextUtils.isEmpty(config.wepKeys[3])) { - wepKeyMap.put(config.configKey(), config.wepKeys[3]); - } - } - return wepKeyMap; - } - - private Map<String, String> createEapPasswordMap(List<WifiConfiguration> configurations) { - Map<String, String> eapPasswordMap = new HashMap<>(); - for (WifiConfiguration config : configurations) { - if (!TextUtils.isEmpty(config.enterpriseConfig.getPassword())) { - eapPasswordMap.put(config.configKey(), config.enterpriseConfig.getPassword()); - } - } - return eapPasswordMap; - } - - private Map<String, WifiConfiguration> createWpaSupplicantLoadData( - List<WifiConfiguration> configurations) { - Map<String, WifiConfiguration> configurationMap = new HashMap<>(); - for (WifiConfiguration config : configurations) { - configurationMap.put(config.configKey(true), config); - } - return configurationMap; - } - - private List<WifiConfiguration> createMaskedWifiConfigurations( - List<WifiConfiguration> configurations) { - List<WifiConfiguration> newConfigurations = new ArrayList<>(); - for (WifiConfiguration config : configurations) { - newConfigurations.add(createMaskedWifiConfiguration(config)); - } - return newConfigurations; - } - - private WifiConfiguration createMaskedWifiConfiguration(WifiConfiguration configuration) { - WifiConfiguration newConfig = new WifiConfiguration(configuration); - if (!TextUtils.isEmpty(configuration.preSharedKey)) { - newConfig.preSharedKey = MASKED_FIELD_VALUE; - } - if (!TextUtils.isEmpty(configuration.wepKeys[0])) { - newConfig.wepKeys[0] = MASKED_FIELD_VALUE; - } - if (!TextUtils.isEmpty(configuration.wepKeys[1])) { - newConfig.wepKeys[1] = MASKED_FIELD_VALUE; - } - if (!TextUtils.isEmpty(configuration.wepKeys[2])) { - newConfig.wepKeys[2] = MASKED_FIELD_VALUE; - } - if (!TextUtils.isEmpty(configuration.wepKeys[3])) { - newConfig.wepKeys[3] = MASKED_FIELD_VALUE; - } - if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) { - newConfig.enterpriseConfig.setPassword(MASKED_FIELD_VALUE); - } - return newConfig; - } - - private Map<String, String> createNetworkExtrasForPasspointConfig(String fqdn) { - Map<String, String> extras = new HashMap<>(); - extras.put(SupplicantStaNetworkHal.ID_STRING_KEY_FQDN, fqdn); - return extras; - } -} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java index 47efed3c4..0958fea61 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.*; import android.app.test.TestAlarmManager; import android.content.Context; +import android.content.pm.PackageManager; import android.net.wifi.WifiConfiguration; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; @@ -60,6 +61,7 @@ public class WifiConfigStoreTest { private static final String TEST_USER_DATA = "UserData"; private static final String TEST_SHARE_DATA = "ShareData"; + private static final String TEST_CREATOR_NAME = "CreatorName"; private static final String TEST_DATA_XML_STRING_FORMAT = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" @@ -91,10 +93,11 @@ public class WifiConfigStoreTest { + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n" + "<int name=\"UserApproved\" value=\"0\" />\n" + "<boolean name=\"MeteredHint\" value=\"false\" />\n" + + "<int name=\"MeteredOverride\" value=\"0\" />\n" + "<boolean name=\"UseExternalScores\" value=\"false\" />\n" + "<int name=\"NumAssociation\" value=\"0\" />\n" + "<int name=\"CreatorUid\" value=\"%d\" />\n" - + "<null name=\"CreatorName\" />\n" + + "<string name=\"CreatorName\">%s</string>\n" + "<null name=\"CreationTime\" />\n" + "<int name=\"LastUpdateUid\" value=\"-1\" />\n" + "<null name=\"LastUpdateName\" />\n" @@ -124,6 +127,7 @@ public class WifiConfigStoreTest { // Test mocks @Mock private Context mContext; + @Mock private PackageManager mPackageManager; private TestAlarmManager mAlarmManager; private TestLooper mLooper; @Mock private Clock mClock; @@ -145,6 +149,8 @@ public class WifiConfigStoreTest { mLooper = new TestLooper(); when(mContext.getSystemService(Context.ALARM_SERVICE)) .thenReturn(mAlarmManager.getAlarmManager()); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME); mUserStore = new MockStoreFile(); mSharedStore = new MockStoreFile(); mStoreData = new MockStoreData(); @@ -363,9 +369,10 @@ public class WifiConfigStoreTest { @Test public void testReadWifiConfigStoreData() throws Exception { // Setup network list. - NetworkListStoreData networkList = new NetworkListStoreData(); + NetworkListStoreData networkList = new NetworkListStoreData(mContext); mWifiConfigStore.registerStoreData(networkList); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.creatorName = TEST_CREATOR_NAME; openNetwork.setIpConfiguration( WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); List<WifiConfiguration> userConfigs = new ArrayList<>(); @@ -383,7 +390,7 @@ public class WifiConfigStoreTest { String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT, openNetwork.configKey().replaceAll("\"", """), openNetwork.SSID.replaceAll("\"", """), - openNetwork.shared, openNetwork.creatorUid, testSsid); + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName, testSsid); byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8); mUserStore.storeRawDataToWrite(xmlBytes); @@ -405,9 +412,10 @@ public class WifiConfigStoreTest { mWifiConfigStore.switchUserStoreAndRead(mUserStore); // Setup network list store data. - NetworkListStoreData networkList = new NetworkListStoreData(); + NetworkListStoreData networkList = new NetworkListStoreData(mContext); mWifiConfigStore.registerStoreData(networkList); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); + openNetwork.creatorName = TEST_CREATOR_NAME; openNetwork.setIpConfiguration( WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy()); List<WifiConfiguration> userConfigs = new ArrayList<>(); @@ -427,7 +435,7 @@ public class WifiConfigStoreTest { String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT, openNetwork.configKey().replaceAll("\"", """), openNetwork.SSID.replaceAll("\"", """), - openNetwork.shared, openNetwork.creatorUid, testSsid); + openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName, testSsid); byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8); mWifiConfigStore.write(true); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java index bdb6b14ed..4fabd9d1e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java @@ -41,6 +41,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.Process; import android.os.SystemClock; import android.os.WorkSource; import android.os.test.TestLooper; @@ -123,7 +124,7 @@ public class WifiConnectivityManagerTest { @Mock private NetworkScoreManager mNetworkScoreManager; @Mock private Clock mClock; @Mock private WifiLastResortWatchdog mWifiLastResortWatchdog; - @Mock private WifiNotificationController mWifiNotificationController; + @Mock private OpenNetworkNotifier mOpenNetworkNotifier; @Mock private WifiMetrics mWifiMetrics; @Mock private WifiNetworkScoreCache mScoreCache; @Captor ArgumentCaptor<ScanResult> mCandidateScanResultCaptor; @@ -294,7 +295,7 @@ public class WifiConnectivityManagerTest { WifiConnectivityManager createConnectivityManager() { return new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiNS, mWifiConnectivityHelper, - mWifiLastResortWatchdog, mWifiNotificationController, mWifiMetrics, + mWifiLastResortWatchdog, mOpenNetworkNotifier, mWifiMetrics, mLooper.getLooper(), mClock, mLocalLog, true, mFrameworkFacade, null, null, null); } @@ -314,7 +315,8 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + verify(mWifiStateMachine).startConnectToNetwork( + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -333,7 +335,8 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_CONNECTED); - verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + verify(mWifiStateMachine).startConnectToNetwork( + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -353,7 +356,7 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleScreenStateChanged(true); verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -373,7 +376,7 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleScreenStateChanged(true); verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -400,7 +403,7 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleScreenStateChanged(true); verify(mWifiStateMachine, times(0)).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -438,7 +441,7 @@ public class WifiConnectivityManagerTest { // Verify that we attempt to connect upto the rate. verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -479,7 +482,7 @@ public class WifiConnectivityManagerTest { // Verify that all the connection attempts went through verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -523,7 +526,7 @@ public class WifiConnectivityManagerTest { // Verify that all the connection attempts went through verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -609,12 +612,12 @@ public class WifiConnectivityManagerTest { } /** - * {@link WifiNotificationController} handles scan results on network selection. + * {@link OpenNetworkNotifier} handles scan results on network selection. * * Expected behavior: ONA handles scan results */ @Test - public void wifiDisconnected_noConnectionCandidate_openNetworkNotificationScanResultsHandled() { + public void wifiDisconnected_noConnectionCandidate_openNetworkNotifierScanResultsHandled() { // no connection candidate selected when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null); @@ -633,37 +636,66 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - verify(mWifiNotificationController).handleScanResults(expectedOpenNetworks); + verify(mOpenNetworkNotifier).handleScanResults(expectedOpenNetworks); } /** - * When wifi is connected, {@link WifiNotificationController} tries to clear the pending - * notification and does not reset notification repeat delay. + * When wifi is connected, {@link OpenNetworkNotifier} handles the Wi-Fi connected behavior. * - * Expected behavior: ONA clears pending notification and does not reset repeat delay. + * Expected behavior: ONA handles connected behavior */ @Test - public void wifiConnected_openNetworkNotificationClearsPendingNotification() { + public void wifiConnected_openNetworkNotifierHandlesConnection() { // Set WiFi to connected state mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_CONNECTED); - verify(mWifiNotificationController).clearPendingNotification(false /* isRepeatDelayReset*/); + verify(mOpenNetworkNotifier).handleWifiConnected(); } /** - * When wifi is connected, {@link WifiNotificationController} handles connection state + * When wifi is connected, {@link OpenNetworkNotifier} handles connection state * change. * * Expected behavior: ONA does not clear pending notification. */ @Test - public void wifiDisconnected_openNetworkNotificationDoesNotClearPendingNotification() { + public void wifiDisconnected_openNetworkNotifierDoesNotClearPendingNotification() { // Set WiFi to disconnected state mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - verify(mWifiNotificationController, never()).clearPendingNotification(anyBoolean()); + verify(mOpenNetworkNotifier, never()).clearPendingNotification(anyBoolean()); + } + + /** + * When a Wi-Fi connection attempt ends, {@link OpenNetworkNotifier} handles the connection + * failure. A failure code that is not {@link WifiMetrics.ConnectionEvent#FAILURE_NONE} + * represents a connection failure. + * + * Expected behavior: ONA handles connection failure. + */ + @Test + public void wifiConnectionEndsWithFailure_openNetworkNotifierHandlesConnectionFailure() { + mWifiConnectivityManager.handleConnectionAttemptEnded( + WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED); + + verify(mOpenNetworkNotifier).handleConnectionFailure(); + } + + /** + * When a Wi-Fi connection attempt ends, {@link OpenNetworkNotifier} does not handle connection + * failure after a successful connection. {@link WifiMetrics.ConnectionEvent#FAILURE_NONE} + * represents a successful connection. + * + * Expected behavior: ONA does nothing. + */ + @Test + public void wifiConnectionEndsWithSuccess_openNetworkNotifierDoesNotHandleConnectionFailure() { + mWifiConnectivityManager.handleConnectionAttemptEnded( + WifiMetrics.ConnectionEvent.FAILURE_NONE); + + verify(mOpenNetworkNotifier, never()).handleConnectionFailure(); } /** @@ -672,24 +704,24 @@ public class WifiConnectivityManagerTest { * Expected behavior: clear pending notification and reset notification repeat delay * */ @Test - public void openNetworkNotificationControllerToggledOnWifiStateChanges() { + public void openNetworkNotifierClearsPendingNotificationOnWifiDisabled() { mWifiConnectivityManager.setWifiEnabled(false); - verify(mWifiNotificationController).clearPendingNotification(true /* isRepeatDelayReset */); + verify(mOpenNetworkNotifier).clearPendingNotification(true /* resetRepeatDelay */); } /** * Verify that the ONA controller tracks screen state changes. */ @Test - public void openNetworkNotificationControllerTracksScreenStateChanges() { + public void openNetworkNotifierTracksScreenStateChanges() { mWifiConnectivityManager.handleScreenStateChanged(false); - verify(mWifiNotificationController).handleScreenStateChanged(false); + verify(mOpenNetworkNotifier).handleScreenStateChanged(false); mWifiConnectivityManager.handleScreenStateChanged(true); - verify(mWifiNotificationController).handleScreenStateChanged(true); + verify(mOpenNetworkNotifier).handleScreenStateChanged(true); } /** @@ -1123,7 +1155,8 @@ public class WifiConnectivityManagerTest { // Verify that WCM receives the scan results and initiates a connection // to the network. - verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + verify(mWifiStateMachine).startConnectToNetwork( + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -1145,21 +1178,22 @@ public class WifiConnectivityManagerTest { // Force a connectivity scan which enables WifiConnectivityManager // to wait for full band scan results. - mWifiConnectivityManager.forceConnectivityScan(); + mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); // No roaming because no full band scan results. verify(mWifiStateMachine, times(0)).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); // Set up as full band scan results. when(mScanData.isAllChannelsScanned()).thenReturn(true); // Force a connectivity scan which enables WifiConnectivityManager // to wait for full band scan results. - mWifiConnectivityManager.forceConnectivityScan(); + mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); // Roaming attempt because full band scan results are available. - verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + verify(mWifiStateMachine).startConnectToNetwork( + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -1252,14 +1286,14 @@ public class WifiConnectivityManagerTest { // its blacklist expiration time hasn't reached yet. when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS / 2); - mWifiConnectivityManager.forceConnectivityScan(); + mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid)); // Force another connectivity scan at BSSID_BLACKLIST_EXPIRE_TIME_MS from when the // BSSID was blacklisted. Verify that the blacklisted BSSId is freed. when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS); - mWifiConnectivityManager.forceConnectivityScan(); + mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); // Verify the BSSID is no longer blacklisted. assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid)); @@ -1412,7 +1446,7 @@ public class WifiConnectivityManagerTest { WifiConnectivityManager.WIFI_STATE_DISCONNECTED); verify(mWifiStateMachine).startConnectToNetwork( - CANDIDATE_NETWORK_ID, WifiStateMachine.SUPPLICANT_BSSID_ANY); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, WifiStateMachine.SUPPLICANT_BSSID_ANY); } /* @@ -1448,7 +1482,8 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + verify(mWifiStateMachine).startConnectToNetwork( + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /* @@ -1468,7 +1503,8 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + verify(mWifiStateMachine).startConnectToNetwork( + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /* @@ -1500,7 +1536,8 @@ public class WifiConnectivityManagerTest { mWifiConnectivityManager.handleConnectionStateChanged( WifiConnectivityManager.WIFI_STATE_DISCONNECTED); - verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + verify(mWifiStateMachine).startConnectToNetwork( + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /** @@ -1590,7 +1627,7 @@ public class WifiConnectivityManagerTest { WifiConnectivityManager.WIFI_STATE_DISCONNECTED); verify(mWifiStateMachine, times(0)).startConnectToNetwork( - CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID); } /* @@ -1652,7 +1689,7 @@ public class WifiConnectivityManagerTest { /** * Dump ONA controller. * - * Expected behavior: {@link WifiNotificationController#dump(FileDescriptor, PrintWriter, + * Expected behavior: {@link OpenNetworkNotifier#dump(FileDescriptor, PrintWriter, * String[])} is invoked. */ @Test @@ -1661,6 +1698,6 @@ public class WifiConnectivityManagerTest { PrintWriter pw = new PrintWriter(sw); mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{}); - verify(mWifiNotificationController).dump(any(), any(), any()); + verify(mOpenNetworkNotifier).dump(any(), any(), any()); } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java index 33aab60e1..fb4e71ef5 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java @@ -169,7 +169,8 @@ public class WifiCountryCodeTest { } /** - * Test if we can reset to the default country code when phone is out of service. + * Test if we can reset to the default country code when phone is out of service, when + * |config_wifi_revert_country_code_on_cellular_loss| is set to true; * Telephony service calls |setCountryCode| with an empty string when phone is out of service. * In this case we should fall back to the default country code. * @throws Exception @@ -184,4 +185,28 @@ public class WifiCountryCodeTest { assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode()); } + /** + * Test if we can keep using the last known country code when phone is out of service, when + * |config_wifi_revert_country_code_on_cellular_loss| is set to false; + * Telephony service calls |setCountryCode| with an empty string when phone is out of service. + * In this case we should keep using the last known country code. + * @throws Exception + */ + @Test + 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 */); + + assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode()); + mWifiCountryCode.setCountryCode(mTelephonyCountryCode); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode()); + // Out of service. + mWifiCountryCode.setCountryCode(""); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode()); + } + } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index 5a139282b..65427beb2 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -16,6 +16,7 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; @@ -37,6 +38,8 @@ import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.hotspot2.PasspointMatch; import com.android.server.wifi.hotspot2.PasspointProvider; import com.android.server.wifi.nano.WifiMetricsProto; +import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount; +import com.android.server.wifi.nano.WifiMetricsProto.PnoScanMetrics; import com.android.server.wifi.nano.WifiMetricsProto.StaEvent; import org.junit.Before; @@ -252,6 +255,24 @@ public class WifiMetricsTest { private static final int NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS = 2; private static final int NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED = 1; private static final int NUM_PARTIAL_SCAN_RESULTS = 73; + private 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; + /** Number of notifications per "Connect to Network" notification type. */ + private static final int[] NUM_CONNECT_TO_NETWORK_NOTIFICATIONS = {0, 10, 20, 30, 40}; + /** Number of notifications per "Connect to Network notification type and action type. */ + private static final int[][] NUM_CONNECT_TO_NETWORK_NOTIFICATION_ACTIONS = { + {0, 1, 2, 3, 4}, + {10, 11, 12, 13, 14}, + {20, 21, 22, 23, 24}, + {30, 31, 32, 33, 34}, + {40, 41, 42, 43, 44}}; + private static final int SIZE_OPEN_NETWORK_RECOMMENDER_BLACKLIST = 10; + private static final boolean IS_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = true; + private static final int NUM_OPEN_NETWORK_CONNECT_MESSAGE_FAILED_TO_SEND = 5; + private static final int NUM_OPEN_NETWORK_RECOMMENDATION_UPDATES = 8; private ScanDetail buildMockScanDetail(boolean hidden, NetworkDetail.HSRelease hSRelease, String capabilities) { @@ -474,6 +495,50 @@ public class WifiMetricsTest { for (int i = 0; i < NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS; i++) { mWifiMetrics.incrementNumPasspointProviderUninstallSuccess(); } + + // increment pno scan metrics + for (int i = 0; i < NUM_PNO_SCAN_ATTEMPTS; i++) { + mWifiMetrics.incrementPnoScanStartAttempCount(); + } + 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(); + } + + // set and increment "connect to network" notification metrics + for (int i = 0; i < NUM_CONNECT_TO_NETWORK_NOTIFICATIONS.length; i++) { + int count = NUM_CONNECT_TO_NETWORK_NOTIFICATIONS[i]; + for (int j = 0; j < count; j++) { + mWifiMetrics.incrementConnectToNetworkNotification(i); + } + } + for (int i = 0; i < NUM_CONNECT_TO_NETWORK_NOTIFICATION_ACTIONS.length; i++) { + int[] actions = NUM_CONNECT_TO_NETWORK_NOTIFICATION_ACTIONS[i]; + for (int j = 0; j < actions.length; j++) { + int count = actions[j]; + for (int k = 0; k < count; k++) { + mWifiMetrics.incrementConnectToNetworkNotificationAction(i, j); + } + } + } + mWifiMetrics.setOpenNetworkRecommenderBlacklistSize( + SIZE_OPEN_NETWORK_RECOMMENDER_BLACKLIST); + mWifiMetrics.setIsWifiNetworksAvailableNotificationEnabled( + IS_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON); + for (int i = 0; i < NUM_OPEN_NETWORK_RECOMMENDATION_UPDATES; i++) { + mWifiMetrics.incrementNumOpenNetworkRecommendationUpdates(); + } + for (int i = 0; i < NUM_OPEN_NETWORK_CONNECT_MESSAGE_FAILED_TO_SEND; i++) { + mWifiMetrics.incrementNumOpenNetworkConnectMessageFailedToSend(); + } } /** @@ -618,6 +683,40 @@ public class WifiMetricsTest { mDecodedProto.numPasspointProviderUninstallSuccess); assertEquals(NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED, mDecodedProto.numPasspointProvidersSuccessfullyConnected); + + PnoScanMetrics pno_metrics = mDecodedProto.pnoScanMetrics; + 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 + : mDecodedProto.connectToNetworkNotificationCount) { + assertEquals(NUM_CONNECT_TO_NETWORK_NOTIFICATIONS[notificationCount.notification], + notificationCount.count); + assertEquals(ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN, + notificationCount.recommender); + } + for (ConnectToNetworkNotificationAndActionCount notificationActionCount + : mDecodedProto.connectToNetworkNotificationActionCount) { + assertEquals(NUM_CONNECT_TO_NETWORK_NOTIFICATION_ACTIONS + [notificationActionCount.notification] + [notificationActionCount.action], + notificationActionCount.count); + assertEquals(ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN, + notificationActionCount.recommender); + } + + assertEquals(SIZE_OPEN_NETWORK_RECOMMENDER_BLACKLIST, + mDecodedProto.openNetworkRecommenderBlacklistSize); + assertEquals(IS_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + mDecodedProto.isWifiNetworksAvailableNotificationOn); + assertEquals(NUM_OPEN_NETWORK_RECOMMENDATION_UPDATES, + mDecodedProto.numOpenNetworkRecommendationUpdates); + assertEquals(NUM_OPEN_NETWORK_CONNECT_MESSAGE_FAILED_TO_SEND, + mDecodedProto.numOpenNetworkConnectMessageFailedToSend); } /** @@ -1171,6 +1270,39 @@ public class WifiMetricsTest { a(WifiMetrics.MAX_CONNECTABLE_BSSID_NETWORK_BUCKET), a(1)); } + /** + * Test Open Network Notification blacklist size and feature state are not cleared when proto + * is dumped. + */ + public void testOpenNetworkNotificationBlacklistSizeAndFeatureStateNotCleared() + throws Exception { + mWifiMetrics.setOpenNetworkRecommenderBlacklistSize( + SIZE_OPEN_NETWORK_RECOMMENDER_BLACKLIST); + mWifiMetrics.setIsWifiNetworksAvailableNotificationEnabled( + IS_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON); + for (int i = 0; i < NUM_OPEN_NETWORK_RECOMMENDATION_UPDATES; i++) { + mWifiMetrics.incrementNumOpenNetworkRecommendationUpdates(); + } + + // This should clear most metrics in mWifiMetrics + dumpProtoAndDeserialize(); + assertEquals(SIZE_OPEN_NETWORK_RECOMMENDER_BLACKLIST, + mDecodedProto.openNetworkRecommenderBlacklistSize); + assertEquals(IS_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + mDecodedProto.isWifiNetworksAvailableNotificationOn); + assertEquals(NUM_OPEN_NETWORK_RECOMMENDATION_UPDATES, + mDecodedProto.numOpenNetworkRecommendationUpdates); + + // Check that blacklist size and feature state persist on next dump but + // others do not. + dumpProtoAndDeserialize(); + assertEquals(SIZE_OPEN_NETWORK_RECOMMENDER_BLACKLIST, + mDecodedProto.openNetworkRecommenderBlacklistSize); + assertEquals(IS_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + mDecodedProto.isWifiNetworksAvailableNotificationOn); + assertEquals(0, mDecodedProto.numOpenNetworkRecommendationUpdates); + } + /** short hand for instantiating an anonymous int array, instead of 'new int[]{a1, a2, ...}' */ private int[] a(int... element) { return element; diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java index 4965a35c2..3d3af364c 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java @@ -23,12 +23,12 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.content.Context; -import android.content.res.Resources; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.WifiInfo; import android.os.SystemClock; +import android.test.mock.MockResources; import android.test.suitebuilder.annotation.SmallTest; import android.util.LocalLog; import android.util.Pair; @@ -41,6 +41,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import java.util.ArrayList; import java.util.HashSet; @@ -52,6 +53,8 @@ import java.util.List; @SmallTest public class WifiNetworkSelectorTest { + private static final int RSSI_BUMP = 1; + /** Sets up test. */ @Before public void setUp() throws Exception { @@ -68,22 +71,6 @@ public class WifiNetworkSelectorTest { mDummyEvaluator.setEvaluatorToSelectCandidate(true); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); - mThresholdMinimumRssi2G = mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); - mThresholdMinimumRssi5G = mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); - mThresholdQualifiedRssi2G = mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); - mThresholdQualifiedRssi5G = mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); - mStayOnNetworkMinimumTxRate = mResource.getInteger( - R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network); - mStayOnNetworkMinimumRxRate = mResource.getInteger( - R.integer.config_wifi_framework_min_rx_rate_for_staying_on_network); - mThresholdSaturatedRssi2G = mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); - mThresholdSaturatedRssi5G = mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz); } /** Cleans up test. */ @@ -145,7 +132,11 @@ public class WifiNetworkSelectorTest { private DummyNetworkEvaluator mDummyEvaluator = new DummyNetworkEvaluator(); @Mock private WifiConfigManager mWifiConfigManager; @Mock private Context mContext; - @Mock private Resources mResource; + + // For simulating the resources, we use a Spy on a MockResource + // (which is really more of a stub than a mock, in spite if its name). + // This is so that we get errors on any calls that we have not explicitly set up. + @Spy private MockResources mResource = new MockResources(); @Mock private WifiInfo mWifiInfo; @Mock private Clock mClock; private LocalLog mLocalLog; @@ -155,40 +146,32 @@ public class WifiNetworkSelectorTest { private int mThresholdQualifiedRssi5G; private int mStayOnNetworkMinimumTxRate; private int mStayOnNetworkMinimumRxRate; - private int mThresholdSaturatedRssi2G; - private int mThresholdSaturatedRssi5G; private void setupContext() { when(mContext.getResources()).thenReturn(mResource); } + private int setupIntegerResource(int resourceName, int value) { + doReturn(value).when(mResource).getInteger(resourceName); + return value; + } + private void setupResources() { - when(mResource.getBoolean( - R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true); - when(mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)) - .thenReturn(-70); - when(mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz)) - .thenReturn(-73); - when(mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz)) - .thenReturn(-82); - when(mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz)) - .thenReturn(-85); - when(mResource.getInteger( - R.integer.config_wifi_framework_max_tx_rate_for_full_scan)) - .thenReturn(8); - when(mResource.getInteger( - R.integer.config_wifi_framework_max_rx_rate_for_full_scan)) - .thenReturn(8); - when(mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz)) - .thenReturn(-57); - when(mResource.getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)) - .thenReturn(-60); + doReturn(true).when(mResource).getBoolean( + R.bool.config_wifi_framework_enable_associated_network_selection); + + mThresholdMinimumRssi2G = setupIntegerResource( + R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz, -79); + mThresholdMinimumRssi5G = setupIntegerResource( + R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz, -76); + mThresholdQualifiedRssi2G = setupIntegerResource( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz, -73); + mThresholdQualifiedRssi5G = setupIntegerResource( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz, -70); + mStayOnNetworkMinimumTxRate = setupIntegerResource( + R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network, 16); + mStayOnNetworkMinimumRxRate = setupIntegerResource( + R.integer.config_wifi_framework_min_rx_rate_for_staying_on_network, 16); } private void setupWifiInfo() { @@ -279,7 +262,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; // Make a network selection. @@ -318,7 +301,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; // Make a network selection. @@ -483,7 +466,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 2457}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 20, mThresholdMinimumRssi2G + 1}; + int[] levels = {mThresholdMinimumRssi2G + 20, mThresholdMinimumRssi2G + RSSI_BUMP}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; // Make a network selection to connect to test1. @@ -534,8 +517,8 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"}; int[] freqs = {2437, 5180, 5181}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1, - mThresholdMinimumRssi5G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP, + mThresholdMinimumRssi5G + RSSI_BUMP}; int[] securities = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = @@ -581,7 +564,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = @@ -961,7 +944,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( @@ -990,7 +973,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3"}; int[] freqs = {2437, 5180}; String[] caps = {"[ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP}; int[] securities = {SECURITY_NONE}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); @@ -1027,7 +1010,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[ESS]", "[ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( @@ -1055,7 +1038,7 @@ public class WifiNetworkSelectorTest { String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java deleted file mode 100644 index 27055a885..000000000 --- a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wifi; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Notification; -import android.app.NotificationManager; -import android.content.Context; -import android.content.res.Resources; -import android.net.wifi.ScanResult; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.test.TestLooper; -import android.provider.Settings; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; - -/** - * Unit tests for {@link WifiNotificationController}. - */ -public class WifiNotificationControllerTest { - - @Mock private Context mContext; - @Mock private Resources mResources; - @Mock private FrameworkFacade mFrameworkFacade; - @Mock private NotificationManager mNotificationManager; - @Mock private UserManager mUserManager; - private WifiNotificationController mNotificationController; - - - /** 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_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(1); - when(mContext.getSystemService(Context.USER_SERVICE)) - .thenReturn(mUserManager); - when(mContext.getResources()).thenReturn(mResources); - - TestLooper mock_looper = new TestLooper(); - mNotificationController = new WifiNotificationController( - mContext, mock_looper.getLooper(), mFrameworkFacade, - mock(Notification.Builder.class)); - mNotificationController.handleScreenStateChanged(true); - } - - private List<ScanDetail> createOpenScanResults() { - List<ScanDetail> scanResults = new ArrayList<>(); - ScanResult scanResult = new ScanResult(); - scanResult.capabilities = "[ESS]"; - scanResults.add(new ScanDetail(scanResult, null /* networkDetail */)); - return scanResults; - } - - /** - * When scan results with open networks are handled, a notification is posted. - */ - @Test - public void handleScanResults_hasOpenNetworks_notificationDisplayed() { - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any()); - } - - /** - * When scan results with no open networks are handled, a notification is not posted. - */ - @Test - public void handleScanResults_emptyList_notificationNotDisplayed() { - mNotificationController.handleScanResults(new ArrayList<>()); - - verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any()); - } - - /** - * When a notification is showing and scan results with no open networks are handled, the - * notification is cleared. - */ - @Test - public void handleScanResults_notificationShown_emptyList_notificationCleared() { - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any()); - - mNotificationController.handleScanResults(new ArrayList<>()); - - verify(mNotificationManager).cancelAsUser(any(), anyInt(), any()); - } - /** - * When a notification is showing, screen is off, and scan results with no open networks are - * handled, the notification is cleared. - */ - @Test - public void handleScanResults_notificationShown_screenOff_emptyList_notificationCleared() { - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any()); - - mNotificationController.handleScreenStateChanged(false); - mNotificationController.handleScanResults(new ArrayList<>()); - - verify(mNotificationManager).cancelAsUser(any(), anyInt(), any()); - } - - /** - * If notification is showing, do not post another notification. - */ - @Test - public void handleScanResults_notificationShowing_doesNotRepostNotification() { - mNotificationController.handleScanResults(createOpenScanResults()); - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any()); - } - - /** - * When {@link WifiNotificationController#clearPendingNotification(boolean)} is called and a - * notification is shown, clear the notification. - */ - @Test - public void clearPendingNotification_clearsNotificationIfOneIsShowing() { - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any()); - - mNotificationController.clearPendingNotification(true); - - verify(mNotificationManager).cancelAsUser(any(), anyInt(), any()); - } - - /** - * When {@link WifiNotificationController#clearPendingNotification(boolean)} is called and a - * notification was not previously shown, do not clear the notification. - */ - @Test - public void clearPendingNotification_doesNotClearNotificationIfNoneShowing() { - mNotificationController.clearPendingNotification(true); - - verify(mNotificationManager, never()).cancelAsUser(any(), anyInt(), any()); - } - - /** - * When screen is off and notification is not displayed, notification is not posted on handling - * new scan results with open networks. - */ - @Test - public void screenOff_handleScanResults_notificationNotDisplayed() { - mNotificationController.handleScreenStateChanged(false); - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any()); - } - - /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} disables the feature. */ - @Test - public void userHasDisallowConfigWifiRestriction_notificationNotDisplayed() { - when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) - .thenReturn(true); - - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any()); - } - - /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} clears the showing notification. */ - @Test - public void userHasDisallowConfigWifiRestriction_showingNotificationIsCleared() { - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any()); - - when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) - .thenReturn(true); - - mNotificationController.handleScanResults(createOpenScanResults()); - - verify(mNotificationManager).cancelAsUser(any(), anyInt(), any()); - } -} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java index 24d3afa18..6f01c8eb2 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java @@ -48,6 +48,18 @@ public class WifiScoreReportTest { private static final int CELLULAR_THRESHOLD_SCORE = 50; + class FakeClock extends Clock { + long mWallClockMillis = 1500000000000L; + int mStepMillis = 1001; + + @Override + public long getWallClockMillis() { + mWallClockMillis += mStepMillis; + return mWallClockMillis; + } + } + + FakeClock mClock; WifiConfiguration mWifiConfiguration; WifiScoreReport mWifiScoreReport; ScanDetailCache mScanDetailCache; @@ -122,7 +134,8 @@ public class WifiScoreReportTest { when(mWifiConfigManager.getScanDetailCacheForNetwork(anyInt())) .thenReturn(mScanDetailCache); when(mContext.getResources()).thenReturn(mResources); - mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager, new Clock()); + mClock = new FakeClock(); + mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager, mClock); } /** diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index 055050deb..e43bdc201 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -400,7 +400,9 @@ public class WifiServiceImplTest { public void testSetWifiEnabledFromNetworkSettingsHolderWhenInAirplaneMode() throws Exception { when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); when(mSettingsStore.isAirplaneModeOn()).thenReturn(true); - when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + when(mContext.checkPermission( + eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) + .thenReturn(PackageManager.PERMISSION_GRANTED); assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true)); verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); } @@ -413,7 +415,9 @@ public class WifiServiceImplTest { public void testSetWifiEnabledFromAppFailsWhenInAirplaneMode() throws Exception { when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); when(mSettingsStore.isAirplaneModeOn()).thenReturn(true); - when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); + when(mContext.checkPermission( + eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) + .thenReturn(PackageManager.PERMISSION_DENIED); assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); } @@ -426,7 +430,9 @@ public class WifiServiceImplTest { public void testSetWifiEnabledFromNetworkSettingsHolderWhenApEnabled() throws Exception { when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED); when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); - when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + when(mContext.checkPermission( + eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) + .thenReturn(PackageManager.PERMISSION_GRANTED); when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true)); verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); @@ -438,7 +444,9 @@ public class WifiServiceImplTest { @Test public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception { when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED); - when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); + when(mContext.checkPermission( + eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) + .thenReturn(PackageManager.PERMISSION_DENIED); when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); verify(mSettingsStore, never()).handleWifiToggled(anyBoolean()); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java index 2e96f3fca..090224e4e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java @@ -30,6 +30,7 @@ import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERR import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -42,9 +43,13 @@ 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; import android.net.LinkProperties; +import android.net.NetworkCapabilities; +import android.net.NetworkFactory; +import android.net.NetworkRequest; import android.net.dhcp.DhcpClient; import android.net.ip.IpClient; import android.net.wifi.IApInterface; @@ -74,6 +79,7 @@ import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -97,6 +103,7 @@ import com.android.internal.util.StateMachine; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.p2p.WifiP2pServiceImpl; +import com.android.server.wifi.util.WifiPermissionsUtil; import org.junit.After; import org.junit.Before; @@ -119,6 +126,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.function.Consumer; /** * Unit tests for {@link com.android.server.wifi.WifiStateMachine}. @@ -133,11 +141,15 @@ public class WifiStateMachineTest { (ActivityManager.isLowRamDeviceStatic() ? WifiStateMachine.NUM_LOG_RECS_VERBOSE_LOW_MEMORY : WifiStateMachine.NUM_LOG_RECS_VERBOSE); - private static final int FRAMEWORK_NETWORK_ID = 7; + private static final int FRAMEWORK_NETWORK_ID = 0; private static final int TEST_RSSI = -54; + private static final int TEST_NETWORK_ID = 54; + private static final int TEST_VALID_NETWORK_SCORE = 54; + private static final int TEST_OUTSCORED_NETWORK_SCORE = 100; private static final int WPS_SUPPLICANT_NETWORK_ID = 5; private static final int WPS_FRAMEWORK_NETWORK_ID = 10; private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\""; + private static final String OP_PACKAGE_NAME = "com.xxx"; private long mBinderToken; @@ -224,7 +236,9 @@ public class WifiStateMachineTest { mAlarmManager.getAlarmManager()); when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( - mock(ConnectivityManager.class)); + mConnectivityManager); + + when(context.getOpPackageName()).thenReturn(OP_PACKAGE_NAME); return context; } @@ -310,6 +324,7 @@ public class WifiStateMachineTest { HandlerThread mP2pThread; HandlerThread mSyncThread; AsyncChannel mWsmAsyncChannel; + AsyncChannel mNetworkFactoryChannel; TestAlarmManager mAlarmManager; MockWifiMonitor mWifiMonitor; TestLooper mLooper; @@ -318,6 +333,7 @@ public class WifiStateMachineTest { FrameworkFacade mFrameworkFacade; IpClient.Callback mIpClientCallback; PhoneStateListener mPhoneStateListener; + NetworkRequest mDefaultNetworkRequest; final ArgumentCaptor<SoftApManager.Listener> mSoftApManagerListenerCaptor = ArgumentCaptor.forClass(SoftApManager.Listener.class); @@ -338,6 +354,7 @@ public class WifiStateMachineTest { @Mock IClientInterface mClientInterface; @Mock IBinder mApInterfaceBinder; @Mock IBinder mClientInterfaceBinder; + @Mock IBinder mPackageManagerBinder; @Mock WifiConfigManager mWifiConfigManager; @Mock WifiNative mWifiNative; @Mock WifiConnectivityManager mWifiConnectivityManager; @@ -345,11 +362,14 @@ public class WifiStateMachineTest { @Mock WifiStateTracker mWifiStateTracker; @Mock PasspointManager mPasspointManager; @Mock SelfRecovery mSelfRecovery; + @Mock WifiPermissionsUtil mWifiPermissionsUtil; @Mock IpClient mIpClient; @Mock TelephonyManager mTelephonyManager; @Mock WrongPasswordNotifier mWrongPasswordNotifier; @Mock Clock mClock; @Mock ScanDetailCache mScanDetailCache; + @Mock BaseWifiDiagnostics mWifiDiagnostics; + @Mock ConnectivityManager mConnectivityManager; public WifiStateMachineTest() throws Exception { } @@ -374,8 +394,7 @@ public class WifiStateMachineTest { when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties); when(mWifiInjector.getKeyStore()).thenReturn(mock(KeyStore.class)); when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class)); - when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn( - mock(BaseWifiDiagnostics.class)); + when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn(mWifiDiagnostics); when(mWifiInjector.makeWificond()).thenReturn(mWificond); when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager); when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner); @@ -390,6 +409,7 @@ public class WifiStateMachineTest { when(mWifiInjector.getWifiMonitor()).thenReturn(mWifiMonitor); when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative); when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery); + when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil); when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager); when(mWifiInjector.getClock()).thenReturn(mClock); @@ -438,15 +458,11 @@ public class WifiStateMachineTest { } }).when(mTelephonyManager).listen(any(PhoneStateListener.class), anyInt()); + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); initializeWsm(); } - private void initializeWsm() throws Exception { - mWsm = new WifiStateMachine(mContext, mFrameworkFacade, mLooper.getLooper(), - mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative, - mWrongPasswordNotifier); - mWsmThread = getWsmHandlerThread(mWsm); - + private void registerAsyncChannel(Consumer<AsyncChannel> consumer, Messenger messenger) { final AsyncChannel channel = new AsyncChannel(); Handler handler = new Handler(mLooper.getLooper()) { @Override @@ -454,7 +470,7 @@ public class WifiStateMachineTest { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mWsmAsyncChannel = channel; + consumer.accept(channel); } else { Log.d(TAG, "Failed to connect Command channel " + this); } @@ -466,15 +482,50 @@ public class WifiStateMachineTest { } }; - channel.connect(mContext, handler, mWsm.getMessenger()); + channel.connect(mContext, handler, messenger); mLooper.dispatchAll(); - /* Now channel is supposed to be connected */ + } + + private void initializeWsm() throws Exception { + mWsm = new WifiStateMachine(mContext, mFrameworkFacade, mLooper.getLooper(), + mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative, + mWrongPasswordNotifier); + mWsmThread = getWsmHandlerThread(mWsm); + + registerAsyncChannel((x) -> { + mWsmAsyncChannel = x; + }, mWsm.getMessenger()); mBinderToken = Binder.clearCallingIdentity(); /* Send the BOOT_COMPLETED message to setup some WSM state. */ mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED); mLooper.dispatchAll(); + + /* Simulate the initial NetworkRequest sent in by ConnectivityService. */ + ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); + verify(mConnectivityManager, atLeast(2)).registerNetworkFactory( + captor.capture(), anyString()); + Messenger networkFactoryMessenger = captor.getAllValues().get(0); + registerAsyncChannel((x) -> { + mNetworkFactoryChannel = x; + }, networkFactoryMessenger); + + mDefaultNetworkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + sendDefaultNetworkRequest(TEST_VALID_NETWORK_SCORE); + } + + /** + * Helper function to resend the cached network request (id == 0) with the specified score. + * Note: If you need to add a separate network request, don't use the builder to create one + * since the new request object will again default to id == 0. + */ + private void sendDefaultNetworkRequest(int score) { + mNetworkFactoryChannel.sendMessage( + NetworkFactory.CMD_REQUEST_NETWORK, score, 0, mDefaultNetworkRequest); + mLooper.dispatchAll(); } @After @@ -490,6 +541,7 @@ public class WifiStateMachineTest { mSyncThread = null; mWsmAsyncChannel = null; mWsm = null; + mNetworkFactoryChannel = null; } @Test @@ -740,18 +792,15 @@ public class WifiStateMachineTest { (Intent) argThat(new WifiEnablingStateIntentMatcher()), any()); } - /** - * Verifies that configs can be removed when in client mode. - */ - @Test - public void canRemoveNetworkConfigInClientMode() throws Exception { + private void canRemoveNetwork() { boolean result; when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true); - initializeAndAddNetworkAndVerifySuccess(); mLooper.startAutoDispatch(); result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0); mLooper.stopAutoDispatch(); + assertTrue(result); + verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt()); } /** @@ -759,23 +808,21 @@ public class WifiStateMachineTest { */ @Test public void canRemoveNetworkConfigWhenWifiDisabled() { - boolean result; - when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true); - mLooper.startAutoDispatch(); - result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0); - mLooper.stopAutoDispatch(); - - assertTrue(result); - verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt()); + canRemoveNetwork(); } + /** - * Verifies that configs can be forgotten when in client mode. + * Verifies that configs can be removed when in client mode. */ @Test - public void canForgetNetworkConfigInClientMode() throws Exception { - when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true); + public void canRemoveNetworkConfigInClientMode() throws Exception { initializeAndAddNetworkAndVerifySuccess(); + canRemoveNetwork(); + } + + private void canForgetNetwork() { + when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true); mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID); mLooper.dispatchAll(); verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt()); @@ -786,10 +833,109 @@ public class WifiStateMachineTest { */ @Test public void canForgetNetworkConfigWhenWifiDisabled() throws Exception { - when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true); - mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID); - mLooper.dispatchAll(); - verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt()); + canForgetNetwork(); + } + + /** + * Verifies that configs can be forgotten when in client mode. + */ + @Test + public void canForgetNetworkConfigInClientMode() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + canForgetNetwork(); + } + + private void canSaveNetworkConfig() { + 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())) + .thenReturn(true); + + mLooper.startAutoDispatch(); + Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config); + mLooper.stopAutoDispatch(); + assertEquals(WifiManager.SAVE_NETWORK_SUCCEEDED, reply.what); + + verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt()); + } + + /** + * Verifies that configs can be saved when not in client mode. + */ + @Test + public void canSaveNetworkConfigWhenWifiDisabled() throws Exception { + canSaveNetworkConfig(); + } + + /** + * Verifies that configs can be saved when in client mode. + */ + @Test + public void canSaveNetworkConfigInClientMode() throws Exception { + loadComponentsInStaMode(); + canSaveNetworkConfig(); + } + + /** + * Verifies that null configs are rejected in SAVE_NETWORK message. + */ + @Test + public void saveNetworkConfigFailsWithNullConfig() throws Exception { + mLooper.startAutoDispatch(); + Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, null); + mLooper.stopAutoDispatch(); + assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what); + + verify(mWifiConfigManager, never()) + .addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); + verify(mWifiConfigManager, never()) + .enableNetwork(anyInt(), anyBoolean(), anyInt()); + } + + /** + * Verifies that configs save fails when the addition of network fails. + */ + @Test + public void saveNetworkConfigFailsWithConfigAddFailure() throws Exception { + WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + + when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) + .thenReturn(new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID)); + + mLooper.startAutoDispatch(); + Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config); + mLooper.stopAutoDispatch(); + assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what); + + verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); + verify(mWifiConfigManager, never()) + .enableNetwork(anyInt(), anyBoolean(), anyInt()); + } + + /** + * Verifies that configs save fails when the enable of network fails. + */ + @Test + public void saveNetworkConfigFailsWithConfigEnableFailure() throws Exception { + WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + + int networkId = 5; + when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) + .thenReturn(new NetworkUpdateResult(networkId)); + when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt())) + .thenReturn(false); + + mLooper.startAutoDispatch(); + Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config); + mLooper.stopAutoDispatch(); + assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what); + + verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()); + verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt()); } /** @@ -829,6 +975,7 @@ public class WifiStateMachineTest { .thenReturn(new NetworkUpdateResult(0)); when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(config)); when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config); + when(mWifiConfigManager.getConfiguredNetworkWithPassword(0)).thenReturn(config); mLooper.startAutoDispatch(); mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config); @@ -937,28 +1084,87 @@ public class WifiStateMachineTest { hiddenNetworkSet); } - @Test - public void connect() throws Exception { - initializeAndAddNetworkAndVerifySuccess(); - when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(true); - when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(0), anyInt())).thenReturn(true); + private void setupAndStartConnectSequence(WifiConfiguration config) throws Exception { + when(mWifiConfigManager.enableNetwork(eq(config.networkId), eq(true), anyInt())) + .thenReturn(true); + when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(config.networkId), anyInt())) + .thenReturn(true); + when(mWifiConfigManager.getConfiguredNetwork(eq(config.networkId))) + .thenReturn(config); + when(mWifiConfigManager.getConfiguredNetworkWithPassword(eq(config.networkId))) + .thenReturn(config); mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); mLooper.dispatchAll(); verify(mWifiNative).removeAllNetworks(); mLooper.startAutoDispatch(); - assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true)); + assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, config.networkId, true)); mLooper.stopAutoDispatch(); + } - verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); - verify(mWifiConnectivityManager).setUserConnectChoice(eq(0)); + private void validateSuccessfulConnectSequence(WifiConfiguration config) { + verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt()); + verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId)); + verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); + verify(mWifiConfigManager).getConfiguredNetworkWithPassword(eq(config.networkId)); + verify(mWifiNative).connectToNetwork(eq(config)); + } + + private void validateFailureConnectSequence(WifiConfiguration config) { + verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt()); + verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId)); + verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId)); + verify(mWifiConfigManager, never()).getConfiguredNetworkWithPassword(eq(config.networkId)); + verify(mWifiNative, never()).connectToNetwork(eq(config)); + } + + /** + * Tests the network connection initiation sequence with the default network request pending + * from WifiNetworkFactory. + * This simulates the connect sequence using the public + * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we invoke + * {@link WifiNative#connectToNetwork(WifiConfiguration)}. + */ + @Test + public void triggerConnect() throws Exception { + loadComponentsInStaMode(); + WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + config.networkId = FRAMEWORK_NETWORK_ID; + setupAndStartConnectSequence(config); + validateSuccessfulConnectSequence(config); + } + + /** + * Tests the network connection initiation sequence with no network request pending from + * from WifiNetworkFactory. + * This simulates the connect sequence using the public + * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we don't invoke + * {@link WifiNative#connectToNetwork(WifiConfiguration)}. + */ + @Test + public void triggerConnectWithNoNetworkRequest() throws Exception { + loadComponentsInStaMode(); + // Change the network score to remove the network request. + sendDefaultNetworkRequest(TEST_OUTSCORED_NETWORK_SCORE); + WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + config.networkId = FRAMEWORK_NETWORK_ID; + setupAndStartConnectSequence(config); + validateFailureConnectSequence(config); + } + + /** + * Tests the entire successful network connection flow. + */ + @Test + public void connect() throws Exception { + triggerConnect(); when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) .thenReturn(mScanDetailCache); when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); - when(mScanDetailCache.get(sBSSID)).thenReturn( + when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); @@ -988,6 +1194,54 @@ public class WifiStateMachineTest { assertEquals("ConnectedState", getCurrentState().getName()); } + /** + * Tests the network connection initiation sequence with no network request pending from + * from WifiNetworkFactory when we're already connected to a different network. + * This simulates the connect sequence using the public + * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we invoke + * {@link WifiNative#connectToNetwork(WifiConfiguration)}. + */ + @Test + public void triggerConnectWithNoNetworkRequestAndAlreadyConnected() throws Exception { + // Simulate the first connection. + connect(); + + // Change the network score to remove the network request. + sendDefaultNetworkRequest(TEST_OUTSCORED_NETWORK_SCORE); + + WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + config.networkId = FRAMEWORK_NETWORK_ID + 1; + setupAndStartConnectSequence(config); + validateSuccessfulConnectSequence(config); + verify(mWifiPermissionsUtil, times(2)).checkNetworkSettingsPermission(anyInt()); + } + + /** + * Tests the network connection initiation sequence from a non-privileged app with no network + * request pending from from WifiNetworkFactory when we're already connected to a different + * network. + * This simulates the connect sequence using the public + * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we don't invoke + * {@link WifiNative#connectToNetwork(WifiConfiguration)}. + */ + @Test + public void triggerConnectWithNoNetworkRequestAndAlreadyConnectedButNonPrivilegedApp() + throws Exception { + // Simulate the first connection. + connect(); + + // Change the network score to remove the network request. + sendDefaultNetworkRequest(TEST_OUTSCORED_NETWORK_SCORE); + + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); + + WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); + config.networkId = FRAMEWORK_NETWORK_ID + 1; + setupAndStartConnectSequence(config); + validateFailureConnectSequence(config); + verify(mWifiPermissionsUtil, times(2)).checkNetworkSettingsPermission(anyInt()); + } + @Test public void connectWithNoEnablePermission() throws Exception { initializeAndAddNetworkAndVerifySuccess(); @@ -1560,31 +1814,90 @@ public class WifiStateMachineTest { } /** - * Verify successful Wps PBC network connection. + * Test that we disconnect from a network if it was removed while we are in the + * ObtainingIpState. + */ + @Test + public void disconnectFromNetworkWhenRemovedWhileObtainingIpAddr() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + + when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(true); + when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(0), anyInt())).thenReturn(true); + + mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); + mLooper.dispatchAll(); + verify(mWifiNative).removeAllNetworks(); + + mLooper.startAutoDispatch(); + assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true)); + mLooper.stopAutoDispatch(); + + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt()); + verify(mWifiConnectivityManager).setUserConnectChoice(eq(0)); + when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) + .thenReturn(mScanDetailCache); + + when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( + getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); + when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( + getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); + + mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); + mLooper.dispatchAll(); + + assertEquals("ObtainingIpState", getCurrentState().getName()); + + // now remove the config + when(mWifiConfigManager.removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt())) + .thenReturn(true); + mWsm.sendMessage(WifiManager.FORGET_NETWORK, FRAMEWORK_NETWORK_ID, MANAGED_PROFILE_UID); + mLooper.dispatchAll(); + verify(mWifiConfigManager).removeNetwork(eq(FRAMEWORK_NETWORK_ID), anyInt()); + + reset(mWifiConfigManager); + + when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID)).thenReturn(null); + + DhcpResults dhcpResults = new DhcpResults(); + dhcpResults.setGateway("1.2.3.4"); + dhcpResults.setIpAddress("192.168.1.100", 0); + dhcpResults.addDns("8.8.8.8"); + dhcpResults.setLeaseDuration(3600); + + injectDhcpSuccess(dhcpResults); + mLooper.dispatchAll(); + + assertEquals("DisconnectingState", getCurrentState().getName()); + } + + /** + * Sunny-day scenario for WPS connections. Verifies that after a START_WPS and + * NETWORK_CONNECTION_EVENT command, WifiStateMachine will have transitioned to ObtainingIpState */ @Test public void wpsPbcConnectSuccess() throws Exception { loadComponentsInStaMode(); mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); mLooper.dispatchAll(); + assertEquals("DisconnectedState", getCurrentState().getName()); - when(mWifiNative.startWpsPbc(eq(sBSSID))).thenReturn(true); WpsInfo wpsInfo = new WpsInfo(); wpsInfo.setup = WpsInfo.PBC; wpsInfo.BSSID = sBSSID; - + when(mWifiNative.removeAllNetworks()).thenReturn(true); + when(mWifiNative.startWpsPbc(anyString())).thenReturn(true); mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo); mLooper.dispatchAll(); - verify(mWifiNative).startWpsPbc(eq(sBSSID)); - assertEquals("WpsRunningState", getCurrentState().getName()); setupMocksForWpsNetworkMigration(); - mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null); mLooper.dispatchAll(); - - assertEquals("DisconnectedState", getCurrentState().getName()); + assertEquals("ObtainingIpState", getCurrentState().getName()); verifyMocksForWpsNetworkMigration(); } @@ -1610,6 +1923,68 @@ public class WifiStateMachineTest { } /** + * Verify that if Supplicant generates multiple networks for a WPS configuration, + * WifiStateMachine cycles into DisconnectedState + */ + @Test + public void wpsPbcConnectFailure_tooManyConfigs() throws Exception { + loadComponentsInStaMode(); + mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); + mLooper.dispatchAll(); + assertEquals("DisconnectedState", getCurrentState().getName()); + + WpsInfo wpsInfo = new WpsInfo(); + wpsInfo.setup = WpsInfo.PBC; + wpsInfo.BSSID = sBSSID; + when(mWifiNative.removeAllNetworks()).thenReturn(true); + when(mWifiNative.startWpsPbc(anyString())).thenReturn(true); + mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo); + mLooper.dispatchAll(); + assertEquals("WpsRunningState", getCurrentState().getName()); + + setupMocksForWpsNetworkMigration(); + doAnswer(new AnswerWithArguments() { + public boolean answer(Map<String, WifiConfiguration> configs, + SparseArray<Map<String, String>> networkExtras) throws Exception { + configs.put("dummy1", new WifiConfiguration()); + configs.put("dummy2", new WifiConfiguration()); + return true; + } + }).when(mWifiNative).migrateNetworksFromSupplicant(any(Map.class), any(SparseArray.class)); + mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null); + mLooper.dispatchAll(); + assertTrue("DisconnectedState".equals(getCurrentState().getName())); + } + + /** + * Verify that when supplicant fails to load networks during WPS, WifiStateMachine cycles into + * DisconnectedState + */ + @Test + public void wpsPbcConnectFailure_migrateNetworksFailure() throws Exception { + loadComponentsInStaMode(); + mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); + mLooper.dispatchAll(); + assertEquals("DisconnectedState", getCurrentState().getName()); + + WpsInfo wpsInfo = new WpsInfo(); + wpsInfo.setup = WpsInfo.PBC; + wpsInfo.BSSID = sBSSID; + when(mWifiNative.removeAllNetworks()).thenReturn(true); + when(mWifiNative.startWpsPbc(anyString())).thenReturn(true); + mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo); + mLooper.dispatchAll(); + assertEquals("WpsRunningState", getCurrentState().getName()); + + setupMocksForWpsNetworkMigration(); + when(mWifiNative.migrateNetworksFromSupplicant(any(Map.class), any(SparseArray.class))) + .thenReturn(false); + mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null); + mLooper.dispatchAll(); + assertEquals("DisconnectedState", getCurrentState().getName()); + } + + /** * Verify successful Wps Pin Display network connection. */ @Test @@ -1634,7 +2009,7 @@ public class WifiStateMachineTest { mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null); mLooper.dispatchAll(); - assertEquals("DisconnectedState", getCurrentState().getName()); + assertEquals("ObtainingIpState", getCurrentState().getName()); verifyMocksForWpsNetworkMigration(); } @@ -1682,6 +2057,7 @@ public class WifiStateMachineTest { verify(mWifiMetrics).incrementNumHalCrashes(); verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_HAL_CRASH)); + verify(mWifiDiagnostics).captureBugReportData(WifiDiagnostics.REPORT_REASON_HAL_CRASH); } @Test @@ -1704,16 +2080,16 @@ public class WifiStateMachineTest { verify(mWifiMetrics).incrementNumWificondCrashes(); verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_WIFICOND_CRASH)); + verify(mWifiDiagnostics).captureBugReportData(WifiDiagnostics.REPORT_REASON_WIFICOND_CRASH); } private void setupMocksForWpsNetworkMigration() { - // Now trigger the network connection event for adding the WPS network. + WifiConfiguration config = new WifiConfiguration(); + config.networkId = WPS_SUPPLICANT_NETWORK_ID; + config.SSID = DEFAULT_TEST_SSID; doAnswer(new AnswerWithArguments() { public boolean answer(Map<String, WifiConfiguration> configs, SparseArray<Map<String, String>> networkExtras) throws Exception { - WifiConfiguration config = new WifiConfiguration(); - config.networkId = WPS_SUPPLICANT_NETWORK_ID; - config.SSID = DEFAULT_TEST_SSID; configs.put("dummy", config); return true; } @@ -1722,6 +2098,10 @@ public class WifiStateMachineTest { .thenReturn(new NetworkUpdateResult(WPS_FRAMEWORK_NETWORK_ID)); when(mWifiConfigManager.enableNetwork(eq(WPS_FRAMEWORK_NETWORK_ID), anyBoolean(), anyInt())) .thenReturn(true); + when(mWifiNative.getFrameworkNetworkId(eq(WPS_FRAMEWORK_NETWORK_ID))).thenReturn( + WPS_FRAMEWORK_NETWORK_ID); + when(mWifiConfigManager.getConfiguredNetwork(eq(WPS_FRAMEWORK_NETWORK_ID))).thenReturn( + config); } private void verifyMocksForWpsNetworkMigration() { @@ -1748,7 +2128,7 @@ public class WifiStateMachineTest { .thenReturn(mScanDetailCache); when(mScanDetailCache.getScanDetail(sBSSID1)).thenReturn( getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1)); - when(mScanDetailCache.get(sBSSID1)).thenReturn( + when(mScanDetailCache.getScanResult(sBSSID1)).thenReturn( getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1).getScanResult()); // This simulates the behavior of roaming to network with |sBSSID1|, |sFreq1|. @@ -1776,7 +2156,7 @@ public class WifiStateMachineTest { .thenReturn(mScanDetailCache); when(mScanDetailCache.getScanDetail(sBSSID1)).thenReturn( getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1)); - when(mScanDetailCache.get(sBSSID1)).thenReturn( + when(mScanDetailCache.getScanResult(sBSSID1)).thenReturn( getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1).getScanResult()); // This simulates the behavior of roaming to network with |sBSSID1|, |sFreq1|. @@ -1856,6 +2236,153 @@ public class WifiStateMachineTest { } /** + * Test that the process uid has full wifiInfo access. + * Also tests that {@link WifiStateMachine#syncRequestConnectionInfo(String)} always + * returns a copy of WifiInfo. + */ + @Test + public void testConnectedIdsAreVisibleFromOwnUid() throws Exception { + assertEquals(Process.myUid(), Binder.getCallingUid()); + WifiInfo wifiInfo = mWsm.getWifiInfo(); + wifiInfo.setBSSID(sBSSID); + wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(sSSID)); + + connect(); + WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName()); + + assertNotEquals(wifiInfo, connectionInfo); + assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID()); + assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID()); + } + + /** + * Test that connected SSID and BSSID are not exposed to an app that does not have the + * appropriate permissions. + * Also tests that {@link WifiStateMachine#syncRequestConnectionInfo(String)} always + * returns a copy of WifiInfo. + */ + @Test + public void testConnectedIdsAreHiddenFromRandomApp() throws Exception { + int actualUid = Binder.getCallingUid(); + int fakeUid = Process.myUid() + 100000; + assertNotEquals(actualUid, fakeUid); + BinderUtil.setUid(fakeUid); + try { + WifiInfo wifiInfo = mWsm.getWifiInfo(); + + // Get into a connected state, with known BSSID and SSID + connect(); + assertEquals(sBSSID, wifiInfo.getBSSID()); + assertEquals(sWifiSsid, wifiInfo.getWifiSsid()); + + when(mWifiPermissionsUtil.canAccessFullConnectionInfo(any(), anyString(), eq(fakeUid), + anyInt())).thenReturn(false); + + WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName()); + + assertNotEquals(wifiInfo, connectionInfo); + assertEquals(WifiSsid.NONE, connectionInfo.getSSID()); + assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID()); + } finally { + BinderUtil.setUid(actualUid); + } + } + + /** + * Test that connected SSID and BSSID are not exposed to an app that does not have the + * appropriate permissions, when canAccessScanResults raises a SecurityException. + * Also tests that {@link WifiStateMachine#syncRequestConnectionInfo(String)} always + * returns a copy of WifiInfo. + */ + @Test + public void testConnectedIdsAreHiddenOnSecurityException() throws Exception { + int actualUid = Binder.getCallingUid(); + int fakeUid = Process.myUid() + 100000; + assertNotEquals(actualUid, fakeUid); + BinderUtil.setUid(fakeUid); + try { + WifiInfo wifiInfo = mWsm.getWifiInfo(); + + // Get into a connected state, with known BSSID and SSID + connect(); + assertEquals(sBSSID, wifiInfo.getBSSID()); + assertEquals(sWifiSsid, wifiInfo.getWifiSsid()); + + when(mWifiPermissionsUtil.canAccessFullConnectionInfo(any(), anyString(), eq(fakeUid), + anyInt())).thenThrow(new SecurityException()); + + WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName()); + + assertNotEquals(wifiInfo, connectionInfo); + assertEquals(WifiSsid.NONE, connectionInfo.getSSID()); + assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID()); + } finally { + BinderUtil.setUid(actualUid); + } + } + + /** + * Test that connected SSID and BSSID are exposed to an app that does have the + * appropriate permissions. + */ + @Test + public void testConnectedIdsAreVisibleFromPermittedApp() throws Exception { + int actualUid = Binder.getCallingUid(); + int fakeUid = Process.myUid() + 100000; + BinderUtil.setUid(fakeUid); + try { + WifiInfo wifiInfo = mWsm.getWifiInfo(); + + // Get into a connected state, with known BSSID and SSID + connect(); + assertEquals(sBSSID, wifiInfo.getBSSID()); + assertEquals(sWifiSsid, wifiInfo.getWifiSsid()); + + when(mWifiPermissionsUtil.canAccessFullConnectionInfo(any(), anyString(), eq(fakeUid), + anyInt())).thenReturn(true); + + WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName()); + + assertNotEquals(wifiInfo, connectionInfo); + assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID()); + assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID()); + // Access to our MAC address uses a different permission, make sure it is not granted + assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getMacAddress()); + } finally { + BinderUtil.setUid(actualUid); + } + } + + /** + * Test that reconnectCommand() triggers connectivity scan when WifiStateMachine + * is in DisconnectedMode. + */ + @Test + public void testReconnectCommandWhenDisconnected() throws Exception { + // Connect to network with |sBSSID|, |sFreq|, and then disconnect. + disconnect(); + + mWsm.reconnectCommand(WifiStateMachine.WIFI_WORK_SOURCE); + mLooper.dispatchAll(); + verify(mWifiConnectivityManager).forceConnectivityScan(WifiStateMachine.WIFI_WORK_SOURCE); + } + + /** + * Test that reconnectCommand() doesn't trigger connectivity scan when WifiStateMachine + * is in ConnectedMode. + */ + @Test + public void testReconnectCommandWhenConnected() throws Exception { + // Connect to network with |sBSSID|, |sFreq|. + connect(); + + mWsm.reconnectCommand(WifiStateMachine.WIFI_WORK_SOURCE); + mLooper.dispatchAll(); + verify(mWifiConnectivityManager, never()) + .forceConnectivityScan(WifiStateMachine.WIFI_WORK_SOURCE); + } + + /** * Adds the network without putting WifiStateMachine into ConnectMode. */ @Test @@ -1872,9 +2399,7 @@ public class WifiStateMachineTest { @Test public void testStartWps_nullWpsInfo() throws Exception { loadComponentsInStaMode(); - mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); - assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest()); - assertEquals("DisconnectedState", getCurrentState().getName()); + mLooper.startAutoDispatch(); Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.START_WPS, 0, 0, null); @@ -1889,9 +2414,6 @@ public class WifiStateMachineTest { @Test public void testSyncDisableNetwork_failure() throws Exception { loadComponentsInStaMode(); - mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); - assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest()); - assertEquals("DisconnectedState", getCurrentState().getName()); when(mWifiConfigManager.disableNetwork(anyInt(), anyInt())).thenReturn(false); mLooper.startAutoDispatch(); @@ -2151,6 +2673,43 @@ public class WifiStateMachineTest { } /** + * Verifies that WifiStateMachine sets and unsets appropriate 'RecentFailureReason' values + * on a WifiConfiguration when it fails association, authentication, or successfully connects + */ + @Test + public void testExtraFailureReason_ApIsBusy() throws Exception { + // Setup CONNECT_MODE & a WifiConfiguration + initializeAndAddNetworkAndVerifySuccess(); + mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); + mLooper.dispatchAll(); + // Trigger a connection to this (CMD_START_CONNECT will actually fail, but it sets up + // targetNetworkId state) + mWsm.sendMessage(WifiStateMachine.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + // Simulate an ASSOCIATION_REJECTION_EVENT, due to the AP being busy + mWsm.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, + ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, sBSSID); + mLooper.dispatchAll(); + verify(mWifiConfigManager).setRecentFailureAssociationStatus(eq(0), + eq(WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA)); + assertEquals("DisconnectedState", getCurrentState().getName()); + + // Simulate an AUTHENTICATION_FAILURE_EVENT, which should clear the ExtraFailureReason + reset(mWifiConfigManager); + mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0, 0, null); + mLooper.dispatchAll(); + verify(mWifiConfigManager).clearRecentFailureReason(eq(0)); + verify(mWifiConfigManager, never()).setRecentFailureAssociationStatus(anyInt(), anyInt()); + + // Simulate a NETWORK_CONNECTION_EVENT which should clear the ExtraFailureReason + reset(mWifiConfigManager); + mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null); + mLooper.dispatchAll(); + verify(mWifiConfigManager).clearRecentFailureReason(eq(0)); + verify(mWifiConfigManager, never()).setRecentFailureAssociationStatus(anyInt(), anyInt()); + } + + /** * Test that the helper method * {@link WifiStateMachine#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)} * returns true when we connect to the last selected network before expiration of @@ -2214,20 +2773,4 @@ public class WifiStateMachineTest { currentConfig.networkId = lastSelectedNetworkId - 1; assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig)); } - - /** - * Test that {@link WifiStateMachine#syncRequestConnectionInfo()} always returns a copy of - * WifiInfo. - */ - @Test - public void testSyncRequestConnectionInfoDoesNotReturnLocalReference() { - WifiInfo wifiInfo = mWsm.getWifiInfo(); - wifiInfo.setBSSID(sBSSID); - wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(sSSID)); - - WifiInfo syncWifiInfo = mWsm.syncRequestConnectionInfo(); - assertEquals(wifiInfo.getSSID(), syncWifiInfo.getSSID()); - assertEquals(wifiInfo.getBSSID(), syncWifiInfo.getBSSID()); - assertFalse(wifiInfo == syncWifiInfo); - } } diff --git a/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java b/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java index 261733144..cca204564 100644 --- a/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java @@ -23,7 +23,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,6 +36,8 @@ import android.net.wifi.IPnoScanEvent; import android.net.wifi.IScanEvent; import android.net.wifi.IWifiScannerImpl; import android.net.wifi.IWificond; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiScanner; import android.test.suitebuilder.annotation.SmallTest; @@ -48,6 +53,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.BitSet; import java.util.HashSet; @@ -60,6 +66,8 @@ import java.util.Set; public class WificondControlTest { private WifiInjector mWifiInjector; private WifiMonitor mWifiMonitor; + private WifiMetrics mWifiMetrics; + private CarrierNetworkConfig mCarrierNetworkConfig; private WificondControl mWificondControl; private static final String TEST_INTERFACE_NAME = "test_wlan_if"; private static final byte[] TEST_SSID = @@ -68,7 +76,7 @@ public class WificondControlTest { new byte[] {(byte) 0x12, (byte) 0xef, (byte) 0xa1, (byte) 0x2c, (byte) 0x97, (byte) 0x8b}; // This the IE buffer which is consistent with TEST_SSID. - private static final byte[] TEST_INFO_ELEMENT = + private static final byte[] TEST_INFO_ELEMENT_SSID = new byte[] { // Element ID for SSID. (byte) 0x00, @@ -76,6 +84,17 @@ public class WificondControlTest { (byte) 0x0b, // This is string "GoogleGuest" 'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'}; + // RSN IE data indicating EAP key management. + private static final byte[] TEST_INFO_ELEMENT_RSN = + new byte[] { + // Element ID for RSN. + (byte) 0x30, + // Length of the element data. + (byte) 0x18, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x02, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x02, (byte) 0x01, (byte) 0x00, + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x01, (byte) 0x00, (byte) 0x00 }; private static final int TEST_FREQUENCY = 2456; private static final int TEST_SIGNAL_MBM = -4500; @@ -86,7 +105,7 @@ public class WificondControlTest { new NativeScanResult() {{ ssid = TEST_SSID; bssid = TEST_BSSID; - infoElement = TEST_INFO_ELEMENT; + infoElement = TEST_INFO_ELEMENT_SSID; frequency = TEST_FREQUENCY; signalMbm = TEST_SIGNAL_MBM; capability = TEST_CAPABILITY; @@ -127,7 +146,10 @@ public class WificondControlTest { public void setUp() throws Exception { mWifiInjector = mock(WifiInjector.class); mWifiMonitor = mock(WifiMonitor.class); - mWificondControl = new WificondControl(mWifiInjector, mWifiMonitor); + mWifiMetrics = mock(WifiMetrics.class); + when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); + mCarrierNetworkConfig = mock(CarrierNetworkConfig.class); + mWificondControl = new WificondControl(mWifiInjector, mWifiMonitor, mCarrierNetworkConfig); } /** @@ -441,7 +463,8 @@ public class WificondControlTest { assertTrue(mWificondControl.tearDownInterfaces()); // getScanResults should fail. - assertEquals(0, mWificondControl.getScanResults().size()); + assertEquals(0, + mWificondControl.getScanResults(WificondControl.SCAN_TYPE_SINGLE_SCAN).size()); } /** @@ -456,7 +479,12 @@ public class WificondControlTest { NativeScanResult[] mockScanResults = {MOCK_NATIVE_SCAN_RESULT}; when(scanner.getScanResults()).thenReturn(mockScanResults); - ArrayList<ScanDetail> returnedScanResults = mWificondControl.getScanResults(); + ArrayList<ScanDetail> returnedScanResults = mWificondControl.getScanResults( + WificondControl.SCAN_TYPE_SINGLE_SCAN); + // The test IEs {@link #TEST_INFO_ELEMENT} doesn't contained RSN IE, which means non-EAP + // AP. So verify carrier network is not checked, since EAP is currently required for a + // carrier network. + verify(mCarrierNetworkConfig, never()).isCarrierNetwork(anyString()); assertEquals(mockScanResults.length, returnedScanResults.size()); // Since NativeScanResult is organized differently from ScanResult, this only checks // a few fields. @@ -471,6 +499,60 @@ public class WificondControlTest { } /** + * Verifies that scan result's carrier network info {@link ScanResult#isCarrierAp} and + * {@link ScanResult#getCarrierApEapType} is set appropriated based on the carrier network + * config. + * + * @throws Exception + */ + @Test + public void testGetScanResultsForCarrierAp() throws Exception { + IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner(); + assertNotNull(scanner); + + // Include RSN IE to indicate EAP key management. + 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.infoElement = out.toByteArray(); + when(scanner.getScanResults()).thenReturn(new NativeScanResult[] {nativeScanResult}); + + // AP associated with a carrier network. + int eapType = WifiEnterpriseConfig.Eap.SIM; + String carrierName = "Test Carrier"; + when(mCarrierNetworkConfig.isCarrierNetwork(new String(nativeScanResult.ssid))) + .thenReturn(true); + when(mCarrierNetworkConfig.getNetworkEapType(new String(nativeScanResult.ssid))) + .thenReturn(eapType); + when(mCarrierNetworkConfig.getCarrierName(new String(nativeScanResult.ssid))) + .thenReturn(carrierName); + ArrayList<ScanDetail> returnedScanResults = mWificondControl.getScanResults( + WificondControl.SCAN_TYPE_SINGLE_SCAN); + assertEquals(1, returnedScanResults.size()); + // Verify returned scan result. + ScanResult scanResult = returnedScanResults.get(0).getScanResult(); + assertArrayEquals(nativeScanResult.ssid, scanResult.SSID.getBytes()); + assertTrue(scanResult.isCarrierAp); + assertEquals(eapType, scanResult.carrierApEapType); + assertEquals(carrierName, scanResult.carrierName); + reset(mCarrierNetworkConfig); + + // AP not associated with a carrier network. + when(mCarrierNetworkConfig.isCarrierNetwork(new String(nativeScanResult.ssid))) + .thenReturn(false); + returnedScanResults = mWificondControl.getScanResults( + WificondControl.SCAN_TYPE_SINGLE_SCAN); + assertEquals(1, returnedScanResults.size()); + // Verify returned scan result. + scanResult = returnedScanResults.get(0).getScanResult(); + assertArrayEquals(nativeScanResult.ssid, scanResult.SSID.getBytes()); + assertFalse(scanResult.isCarrierAp); + assertEquals(ScanResult.UNSPECIFIED, scanResult.carrierApEapType); + assertEquals(null, scanResult.carrierName); + } + + /** * Verifies that Scan() can convert input parameters to SingleScanSettings correctly. */ @Test @@ -581,7 +663,7 @@ public class WificondControlTest { /** * Verifies that WificondControl can invoke WifiMonitor broadcast methods upon pno scan - * reuslt event. + * result event. */ @Test public void testPnoScanResultEvent() throws Exception { @@ -592,11 +674,48 @@ public class WificondControlTest { IPnoScanEvent pnoScanEvent = messageCaptor.getValue(); assertNotNull(pnoScanEvent); pnoScanEvent.OnPnoNetworkFound(); - verify(mWifiMonitor).broadcastPnoScanResultEvent(any(String.class)); } /** + * Verifies that WificondControl can invoke WifiMetrics pno scan count methods upon pno event. + */ + @Test + public void testPnoScanEventsForMetrics() throws Exception { + IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner(); + + ArgumentCaptor<IPnoScanEvent> messageCaptor = ArgumentCaptor.forClass(IPnoScanEvent.class); + verify(scanner).subscribePnoScanEvents(messageCaptor.capture()); + IPnoScanEvent pnoScanEvent = messageCaptor.getValue(); + assertNotNull(pnoScanEvent); + + pnoScanEvent.OnPnoNetworkFound(); + verify(mWifiMetrics).incrementPnoFoundNetworkEventCount(); + + pnoScanEvent.OnPnoScanFailed(); + verify(mWifiMetrics).incrementPnoScanFailedCount(); + + pnoScanEvent.OnPnoScanOverOffloadStarted(); + verify(mWifiMetrics).incrementPnoScanStartedOverOffloadCount(); + + pnoScanEvent.OnPnoScanOverOffloadFailed(0); + verify(mWifiMetrics).incrementPnoScanFailedOverOffloadCount(); + } + + /** + * Verifies that startPnoScan() can invoke WifiMetrics pno scan count methods correctly. + */ + @Test + public void testStartPnoScanForMetrics() throws Exception { + IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner(); + + when(scanner.startPnoScan(any(PnoSettings.class))).thenReturn(false); + assertFalse(mWificondControl.startPnoScan(TEST_PNO_SETTINGS)); + verify(mWifiMetrics).incrementPnoScanStartAttempCount(); + verify(mWifiMetrics).incrementPnoScanFailedCount(); + } + + /** * Verifies that abortScan() calls underlying wificond. */ @Test diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java index 806300423..61143d9bb 100644 --- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java @@ -19,6 +19,7 @@ package com.android.server.wifi.aware; import static android.hardware.wifi.V1_0.NanDataPathChannelCfg.CHANNEL_NOT_REQUESTED; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -83,8 +84,9 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.lang.reflect.Field; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Unit test harness for WifiAwareDataPathStateManager class. @@ -99,7 +101,6 @@ public class WifiAwareDataPathStateManagerTest { @Mock private WifiAwareNativeManager mMockNativeManager; @Mock private WifiAwareNativeApi mMockNative; @Mock private Context mMockContext; - @Mock private IWifiAwareManager mMockAwareService; @Mock private ConnectivityManager mMockCm; @Mock private INetworkManagementService mMockNwMgt; @Mock private WifiAwareDataPathStateManager.NetworkInterfaceWrapper mMockNetworkInterface; @@ -172,7 +173,8 @@ public class WifiAwareDataPathStateManagerTest { when(mPermissionsWrapperMock.getUidPermission(eq(Manifest.permission.CONNECTIVITY_INTERNAL), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); - installDataPathStateManagerMocks(); + mDut.mDataPathMgr.mNwService = mMockNwMgt; + mDut.mDataPathMgr.mNiWrapper = mMockNetworkInterface; } /** @@ -252,7 +254,7 @@ public class WifiAwareDataPathStateManagerTest { mMockLooper.dispatchAll(); } - verifyNoMoreInteractions(mMockNative); + verifyNoMoreInteractions(mMockNative, mMockNwMgt); } /** @@ -273,12 +275,12 @@ public class WifiAwareDataPathStateManagerTest { anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); // (0) initialize - DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId, + DataPathEndPointInfo res = initDataPathEndPoint(true, clientId, pubSubId, requestorId, peerDiscoveryMac, inOrder, inOrderM, false); // (1) request network NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle, pmk, - null, false); + null, false, 0); Message reqNetworkMsg = Message.obtain(); reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; @@ -288,7 +290,7 @@ public class WifiAwareDataPathStateManagerTest { mMockLooper.dispatchAll(); // failure: no interactions with connectivity manager or native manager - verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock); + verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt); } /** @@ -309,12 +311,12 @@ public class WifiAwareDataPathStateManagerTest { ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); // (0) initialize - DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId, + DataPathEndPointInfo res = initDataPathEndPoint(true, clientId, pubSubId, requestorId, peerDiscoveryMac, inOrder, inOrderM, true); // (1) request network NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle, - null, null, true); + null, null, true, 0); Message reqNetworkMsg = Message.obtain(); reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; @@ -342,7 +344,302 @@ public class WifiAwareDataPathStateManagerTest { mMockLooper.dispatchAll(); // failure if there's further activity - verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock); + verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt); + } + + /** + * Validate multiple NDPs created on a single NDI. Most importantly that the interface is + * set up on first NDP and torn down on last NDP - and not when one or the other is created or + * deleted. + * + * Procedure: + * - create NDP 1, 2, and 3 (interface up only on first) + * - delete NDP 2, 1, and 3 (interface down only on last) + */ + @Test + public void testMultipleNdpsOnSingleNdi() throws Exception { + final int clientId = 123; + final byte pubSubId = 58; + final int requestorId = 1341234; + final int ndpId = 2; + + final int[] startOrder = {0, 1, 2}; + final int[] endOrder = {1, 0, 2}; + int networkRequestId = 0; + + ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); + ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); + InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mMockNwMgt); + InOrder inOrderM = inOrder(mAwareMetricsMock); + + NetworkRequest[] nrs = new NetworkRequest[3]; + DataPathEndPointInfo[] ress = new DataPathEndPointInfo[3]; + Messenger[] agentMessengers = new Messenger[3]; + Messenger messenger = null; + boolean first = true; + for (int i : startOrder) { + networkRequestId += 1; + byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false); + byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false); + peerDiscoveryMac[5] = (byte) (peerDiscoveryMac[5] + i); + peerDataPathMac[5] = (byte) (peerDataPathMac[5] + i); + + // (0) initialize + ress[i] = initDataPathEndPoint(first, clientId, (byte) (pubSubId + i), + requestorId + i, peerDiscoveryMac, inOrder, inOrderM, false); + if (first) { + messenger = ress[i].mMessenger; + } + + // (1) request network + nrs[i] = getSessionNetworkRequest(clientId, ress[i].mSessionId, ress[i].mPeerHandle, + null, null, false, networkRequestId); + + Message reqNetworkMsg = Message.obtain(); + reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; + reqNetworkMsg.obj = nrs[i]; + reqNetworkMsg.arg1 = 0; + messenger.send(reqNetworkMsg); + mMockLooper.dispatchAll(); + inOrder.verify(mMockNative).initiateDataPath(transactionId.capture(), + eq(requestorId + i), + eq(CHANNEL_NOT_REQUESTED), anyInt(), eq(peerDiscoveryMac), + eq(sAwareInterfacePrefix + "0"), eq(null), + eq(null), eq(false), any()); + + mDut.onInitiateDataPathResponseSuccess(transactionId.getValue(), ndpId + i); + mMockLooper.dispatchAll(); + + // (2) get confirmation + mDut.onDataPathConfirmNotification(ndpId + i, peerDataPathMac, true, 0, null); + mMockLooper.dispatchAll(); + if (first) { + inOrder.verify(mMockNwMgt).setInterfaceUp(anyString()); + inOrder.verify(mMockNwMgt).enableIpv6(anyString()); + + first = false; + } + inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(), + any(), anyInt(), any()); + agentMessengers[i] = messengerCaptor.getValue(); + inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS), + eq(false), anyLong()); + inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any()); + } + + // (3) end data-path (unless didn't get confirmation) + int index = 0; + for (int i: endOrder) { + Message endNetworkReqMsg = Message.obtain(); + endNetworkReqMsg.what = NetworkFactory.CMD_CANCEL_REQUEST; + endNetworkReqMsg.obj = nrs[i]; + messenger.send(endNetworkReqMsg); + + Message endNetworkUsageMsg = Message.obtain(); + endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED; + agentMessengers[i].send(endNetworkUsageMsg); + mMockLooper.dispatchAll(); + + inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId + i)); + + mDut.onEndDataPathResponse(transactionId.getValue(), true, 0); + mDut.onDataPathEndNotification(ndpId + i); + mMockLooper.dispatchAll(); + + if (index++ == endOrder.length - 1) { + inOrder.verify(mMockNwMgt).setInterfaceDown(anyString()); + } + inOrderM.verify(mAwareMetricsMock).recordNdpSessionDuration(anyLong()); + } + + verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt); + } + + /** + * Validate that multiple NDP requests which resolve to the same canonical request are treated + * as one. + */ + @Test + public void testMultipleIdenticalRequests() throws Exception { + final int numRequestsPre = 6; + final int numRequestsPost = 5; + final int clientId = 123; + final int ndpId = 5; + final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false); + final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false); + NetworkRequest[] nrs = new NetworkRequest[numRequestsPre + numRequestsPost]; + + ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); + ArgumentCaptor<Messenger> agentMessengerCaptor = ArgumentCaptor.forClass(Messenger.class); + + InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mMockNwMgt); + InOrder inOrderM = inOrder(mAwareMetricsMock); + + // (1) initialize all clients + Messenger messenger = initOobDataPathEndPoint(true, 1, clientId, inOrder, inOrderM); + for (int i = 1; i < numRequestsPre + numRequestsPost; ++i) { + initOobDataPathEndPoint(false, 1, clientId + i, inOrder, inOrderM); + } + + // (2) make 3 network requests (all identical under the hood) + for (int i = 0; i < numRequestsPre; ++i) { + nrs[i] = getDirectNetworkRequest(clientId + i, + WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, null, + null, i); + + Message reqNetworkMsg = Message.obtain(); + reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; + reqNetworkMsg.obj = nrs[i]; + reqNetworkMsg.arg1 = 0; + messenger.send(reqNetworkMsg); + } + mMockLooper.dispatchAll(); + + // (3) verify the start NDP HAL request + inOrder.verify(mMockNative).initiateDataPath(transactionId.capture(), eq(0), + eq(CHANNEL_NOT_REQUESTED), anyInt(), eq(peerDiscoveryMac), + eq(sAwareInterfacePrefix + "0"), eq(null), eq(null), eq(true), any()); + + // (4) unregister request #0 (the primary) + Message endNetworkReqMsg = Message.obtain(); + endNetworkReqMsg.what = NetworkFactory.CMD_CANCEL_REQUEST; + endNetworkReqMsg.obj = nrs[0]; + messenger.send(endNetworkReqMsg); + mMockLooper.dispatchAll(); + + // (5) respond to the registration request + mDut.onInitiateDataPathResponseSuccess(transactionId.getValue(), ndpId); + mMockLooper.dispatchAll(); + + // (6) unregister request #1 + endNetworkReqMsg = Message.obtain(); + endNetworkReqMsg.what = NetworkFactory.CMD_CANCEL_REQUEST; + endNetworkReqMsg.obj = nrs[1]; + messenger.send(endNetworkReqMsg); + mMockLooper.dispatchAll(); + + // (7) confirm the NDP creation + mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0, null); + mMockLooper.dispatchAll(); + + inOrder.verify(mMockNwMgt).setInterfaceUp(anyString()); + inOrder.verify(mMockNwMgt).enableIpv6(anyString()); + inOrder.verify(mMockCm).registerNetworkAgent(agentMessengerCaptor.capture(), any(), any(), + any(), anyInt(), any()); + inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS), + eq(true), anyLong()); + inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any()); + + // (8) execute 'post' requests + for (int i = numRequestsPre; i < numRequestsPre + numRequestsPost; ++i) { + nrs[i] = getDirectNetworkRequest(clientId + i, + WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, null, + null, i); + + Message reqNetworkMsg = Message.obtain(); + reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; + reqNetworkMsg.obj = nrs[i]; + reqNetworkMsg.arg1 = 0; + messenger.send(reqNetworkMsg); + } + mMockLooper.dispatchAll(); + + // (9) unregister all requests + for (int i = 2; i < numRequestsPre + numRequestsPost; ++i) { + endNetworkReqMsg = Message.obtain(); + endNetworkReqMsg.what = NetworkFactory.CMD_CANCEL_REQUEST; + endNetworkReqMsg.obj = nrs[i]; + messenger.send(endNetworkReqMsg); + mMockLooper.dispatchAll(); + } + + Message endNetworkUsageMsg = Message.obtain(); + endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED; + agentMessengerCaptor.getValue().send(endNetworkUsageMsg); + mMockLooper.dispatchAll(); + + // (10) verify that NDP torn down + inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId)); + + mDut.onEndDataPathResponse(transactionId.getValue(), true, 0); + mDut.onDataPathEndNotification(ndpId); + mMockLooper.dispatchAll(); + + inOrder.verify(mMockNwMgt).setInterfaceDown(anyString()); + inOrderM.verify(mAwareMetricsMock).recordNdpSessionDuration(anyLong()); + + verifyNoMoreInteractions(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mAwareMetricsMock, mMockNwMgt); + } + + /** + * Validate that multiple NDP requests to the same peer target different NDIs. + */ + @Test + public void testMultipleNdi() throws Exception { + final int numNdis = 5; + final int clientId = 123; + final int ndpId = 5; + final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false); + final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false); + + ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); + ArgumentCaptor<String> ifNameCaptor = ArgumentCaptor.forClass(String.class); + + InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mMockNwMgt); + InOrder inOrderM = inOrder(mAwareMetricsMock); + + // (1) initialize all clients + Messenger messenger = initOobDataPathEndPoint(true, numNdis, clientId, inOrder, inOrderM); + for (int i = 1; i < numNdis + 3; ++i) { + initOobDataPathEndPoint(false, numNdis, clientId + i, inOrder, inOrderM); + } + + // (2) make N network requests: each unique + Set<String> interfaces = new HashSet<>(); + for (int i = 0; i < numNdis + 1; ++i) { + byte[] pmk = new byte[32]; + pmk[0] = (byte) i; + + NetworkRequest nr = getDirectNetworkRequest(clientId + i, + WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, pmk, + null, i); + + Message reqNetworkMsg = Message.obtain(); + reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; + reqNetworkMsg.obj = nr; + reqNetworkMsg.arg1 = 0; + messenger.send(reqNetworkMsg); + mMockLooper.dispatchAll(); + + if (i < numNdis) { + inOrder.verify(mMockNative).initiateDataPath(transactionId.capture(), eq(0), + eq(CHANNEL_NOT_REQUESTED), anyInt(), eq(peerDiscoveryMac), + ifNameCaptor.capture(), eq(pmk), eq(null), eq(true), any()); + interfaces.add(ifNameCaptor.getValue()); + + mDut.onInitiateDataPathResponseSuccess(transactionId.getValue(), ndpId + i); + mDut.onDataPathConfirmNotification(ndpId + i, peerDataPathMac, true, 0, null); + mMockLooper.dispatchAll(); + + inOrder.verify(mMockNwMgt).setInterfaceUp(anyString()); + inOrder.verify(mMockNwMgt).enableIpv6(anyString()); + inOrder.verify(mMockCm).registerNetworkAgent(any(), any(), any(), any(), anyInt(), + any()); + inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS), + eq(true), anyLong()); + inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any()); + } + } + + // verify that each interface name is unique + assertEquals("Number of unique interface names", numNdis, interfaces.size()); + + verifyNoMoreInteractions(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mAwareMetricsMock, mMockNwMgt); } /* @@ -563,12 +860,12 @@ public class WifiAwareDataPathStateManagerTest { InOrder inOrderM = inOrder(mAwareMetricsMock); // (0) initialize - DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId, + DataPathEndPointInfo res = initDataPathEndPoint(true, clientId, pubSubId, requestorId, peerDiscoveryMac, inOrder, inOrderM, doPublish); // (1) request network NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle, pmk, - null, doPublish); + null, doPublish, 0); // corrupt the network specifier: reverse the role (so it's mis-matched) WifiAwareNetworkSpecifier ns = @@ -603,7 +900,7 @@ public class WifiAwareDataPathStateManagerTest { eq(ndpId), eq(""), eq(null), eq(null), anyBoolean(), any()); } - verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock); + verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt); } private void testDataPathInitiatorResponderInvalidUidUtility(boolean doPublish, @@ -619,12 +916,12 @@ public class WifiAwareDataPathStateManagerTest { InOrder inOrderM = inOrder(mAwareMetricsMock); // (0) initialize - DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId, + DataPathEndPointInfo res = initDataPathEndPoint(true, clientId, pubSubId, requestorId, peerDiscoveryMac, inOrder, inOrderM, doPublish); // (1) create network request NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle, pmk, - null, doPublish); + null, doPublish, 0); // (2) corrupt request's UID WifiAwareNetworkSpecifier ns = @@ -660,7 +957,7 @@ public class WifiAwareDataPathStateManagerTest { eq(ndpId), eq(""), eq(null), eq(null), anyBoolean(), any()); } - verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock); + verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt); } private void testDataPathInitiatorUtility(boolean useDirect, boolean provideMac, @@ -678,7 +975,8 @@ public class WifiAwareDataPathStateManagerTest { ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); - InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback); + InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mMockNwMgt); InOrder inOrderM = inOrder(mAwareMetricsMock); if (!providePmk) { @@ -694,7 +992,7 @@ public class WifiAwareDataPathStateManagerTest { } // (0) initialize - DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId, + DataPathEndPointInfo res = initDataPathEndPoint(true, clientId, pubSubId, requestorId, peerDiscoveryMac, inOrder, inOrderM, false); // (1) request network @@ -703,11 +1001,11 @@ public class WifiAwareDataPathStateManagerTest { nr = getDirectNetworkRequest(clientId, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null, - providePassphrase ? passphrase : null); + providePassphrase ? passphrase : null, 0); } else { nr = getSessionNetworkRequest(clientId, res.mSessionId, provideMac ? res.mPeerHandle : null, providePmk ? pmk : null, - providePassphrase ? passphrase : null, false); + providePassphrase ? passphrase : null, false, 0); } Message reqNetworkMsg = Message.obtain(); @@ -737,6 +1035,8 @@ public class WifiAwareDataPathStateManagerTest { mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0, peerToken.getBytes()); mMockLooper.dispatchAll(); + inOrder.verify(mMockNwMgt).setInterfaceUp(anyString()); + inOrder.verify(mMockNwMgt).enableIpv6(anyString()); inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(), any(), anyInt(), any()); inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS), @@ -763,16 +1063,16 @@ public class WifiAwareDataPathStateManagerTest { Message endNetworkUsageMsg = Message.obtain(); endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED; messengerCaptor.getValue().send(endNetworkUsageMsg); - - mMockLooper.dispatchAll(); - inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId)); mDut.onEndDataPathResponse(transactionId.getValue(), true, 0); mDut.onDataPathEndNotification(ndpId); mMockLooper.dispatchAll(); + + inOrder.verify(mMockNwMgt).setInterfaceDown(anyString()); + inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId)); inOrderM.verify(mAwareMetricsMock).recordNdpSessionDuration(anyLong()); } - verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock); + verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt); } private void testDataPathResponderUtility(boolean useDirect, boolean provideMac, @@ -790,7 +1090,8 @@ public class WifiAwareDataPathStateManagerTest { ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); - InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback); + InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mMockNwMgt); InOrder inOrderM = inOrder(mAwareMetricsMock); if (providePmk) { @@ -800,7 +1101,7 @@ public class WifiAwareDataPathStateManagerTest { } // (0) initialize - DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId, + DataPathEndPointInfo res = initDataPathEndPoint(true, clientId, pubSubId, requestorId, peerDiscoveryMac, inOrder, inOrderM, true); // (1) request network @@ -809,11 +1110,11 @@ public class WifiAwareDataPathStateManagerTest { nr = getDirectNetworkRequest(clientId, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null, - providePassphrase ? passphrase : null); + providePassphrase ? passphrase : null, 0); } else { nr = getSessionNetworkRequest(clientId, res.mSessionId, provideMac ? res.mPeerHandle : null, providePmk ? pmk : null, - providePassphrase ? passphrase : null, true); + providePassphrase ? passphrase : null, true, 0); } Message reqNetworkMsg = Message.obtain(); @@ -837,6 +1138,8 @@ public class WifiAwareDataPathStateManagerTest { mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0, peerToken.getBytes()); mMockLooper.dispatchAll(); + inOrder.verify(mMockNwMgt).setInterfaceUp(anyString()); + inOrder.verify(mMockNwMgt).enableIpv6(anyString()); inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(), any(), anyInt(), any()); inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS), @@ -864,35 +1167,23 @@ public class WifiAwareDataPathStateManagerTest { endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED; messengerCaptor.getValue().send(endNetworkUsageMsg); - mMockLooper.dispatchAll(); - inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId)); mDut.onEndDataPathResponse(transactionId.getValue(), true, 0); mDut.onDataPathEndNotification(ndpId); mMockLooper.dispatchAll(); + + inOrder.verify(mMockNwMgt).setInterfaceDown(anyString()); + inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId)); inOrderM.verify(mAwareMetricsMock).recordNdpSessionDuration(anyLong()); } - verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock); - } - - private void installDataPathStateManagerMocks() throws Exception { - Field field = WifiAwareStateManager.class.getDeclaredField("mDataPathMgr"); - field.setAccessible(true); - Object mDataPathMgr = field.get(mDut); - - field = WifiAwareDataPathStateManager.class.getDeclaredField("mNwService"); - field.setAccessible(true); - field.set(mDataPathMgr, mMockNwMgt); - - field = WifiAwareDataPathStateManager.class.getDeclaredField("mNiWrapper"); - field.setAccessible(true); - field.set(mDataPathMgr, mMockNetworkInterface); + verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt); } private NetworkRequest getSessionNetworkRequest(int clientId, int sessionId, - PeerHandle peerHandle, byte[] pmk, String passphrase, boolean doPublish) + PeerHandle peerHandle, byte[] pmk, String passphrase, boolean doPublish, int requestId) throws Exception { - final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mMockAwareService); + final IWifiAwareManager mockAwareService = mock(IWifiAwareManager.class); + final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mockAwareService); final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final PublishConfig publishConfig = new PublishConfig.Builder().build(); final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); @@ -910,30 +1201,33 @@ public class WifiAwareDataPathStateManagerTest { DiscoverySessionCallback mockSessionCallback = mock( DiscoverySessionCallback.class); + InOrder inOrderS = inOrder(mockAwareService, mockCallback, mockSessionCallback); + mgr.attach(mMockLooperHandler, configRequest, mockCallback, null); - verify(mMockAwareService).connect(any(), any(), + inOrderS.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); - clientProxyCallback.getValue().onConnectSuccess(clientId); + IWifiAwareEventCallback iwaec = clientProxyCallback.getValue(); + iwaec.onConnectSuccess(clientId); mMockLooper.dispatchAll(); - verify(mockCallback).onAttached(sessionCaptor.capture()); + inOrderS.verify(mockCallback).onAttached(sessionCaptor.capture()); if (doPublish) { sessionCaptor.getValue().publish(publishConfig, mockSessionCallback, mMockLooperHandler); - verify(mMockAwareService).publish(eq(clientId), eq(publishConfig), + inOrderS.verify(mockAwareService).publish(eq(clientId), eq(publishConfig), sessionProxyCallback.capture()); } else { sessionCaptor.getValue().subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler); - verify(mMockAwareService).subscribe(eq(clientId), eq(subscribeConfig), + inOrderS.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig), sessionProxyCallback.capture()); } sessionProxyCallback.getValue().onSessionStarted(sessionId); mMockLooper.dispatchAll(); if (doPublish) { - verify(mockSessionCallback).onPublishStarted( + inOrderS.verify(mockSessionCallback).onPublishStarted( (PublishDiscoverySession) discoverySession.capture()); } else { - verify(mockSessionCallback).onSubscribeStarted( + inOrderS.verify(mockSessionCallback).onSubscribeStarted( (SubscribeDiscoverySession) discoverySession.capture()); } @@ -957,13 +1251,14 @@ public class WifiAwareDataPathStateManagerTest { nc.setLinkDownstreamBandwidthKbps(1); nc.setSignalStrength(1); - return new NetworkRequest(nc, 0, 0, NetworkRequest.Type.NONE); + return new NetworkRequest(nc, 0, requestId, NetworkRequest.Type.NONE); } private NetworkRequest getDirectNetworkRequest(int clientId, int role, byte[] peer, - byte[] pmk, String passphrase) throws Exception { + byte[] pmk, String passphrase, int requestId) throws Exception { + final IWifiAwareManager mockAwareService = mock(IWifiAwareManager.class); final ConfigRequest configRequest = new ConfigRequest.Builder().build(); - final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mMockAwareService); + final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mockAwareService); ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( WifiAwareSession.class); @@ -973,7 +1268,7 @@ public class WifiAwareDataPathStateManagerTest { AttachCallback mockCallback = mock(AttachCallback.class); mgr.attach(mMockLooperHandler, configRequest, mockCallback, null); - verify(mMockAwareService).connect(any(), any(), + verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -997,64 +1292,25 @@ public class WifiAwareDataPathStateManagerTest { nc.setLinkDownstreamBandwidthKbps(1); nc.setSignalStrength(1); - return new NetworkRequest(nc, 0, 0, NetworkRequest.Type.REQUEST); + return new NetworkRequest(nc, 0, requestId, NetworkRequest.Type.REQUEST); } - private DataPathEndPointInfo initDataPathEndPoint(int clientId, byte pubSubId, - int requestorId, byte[] peerDiscoveryMac, InOrder inOrder, InOrder inOrderM, - boolean doPublish) + private DataPathEndPointInfo initDataPathEndPoint(boolean isFirstIteration, int clientId, + byte pubSubId, int requestorId, byte[] peerDiscoveryMac, InOrder inOrder, + InOrder inOrderM, boolean doPublish) throws Exception { - final int pid = 2000; - final String callingPackage = "com.android.somePackage"; final String someMsg = "some arbitrary message from peer"; - final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final PublishConfig publishConfig = new PublishConfig.Builder().build(); final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); - Capabilities capabilities = new Capabilities(); - capabilities.maxNdiInterfaces = 1; - ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); - ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class); - - // (0) start/registrations - inOrder.verify(mMockCm).registerNetworkFactory(messengerCaptor.capture(), - strCaptor.capture()); - collector.checkThat("factory name", "WIFI_AWARE_FACTORY", equalTo(strCaptor.getValue())); - // (1) get capabilities - mDut.queryCapabilities(); - mMockLooper.dispatchAll(); - inOrder.verify(mMockNative).getCapabilities(transactionId.capture()); - mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), capabilities); - mMockLooper.dispatchAll(); - - // (2) enable usage - mDut.enableUsage(); - mMockLooper.dispatchAll(); - inOrderM.verify(mAwareMetricsMock).recordEnableUsage(); - - // (3) create client & session & rx message - mDut.connect(clientId, Process.myUid(), pid, callingPackage, mMockCallback, configRequest, - false); - mMockLooper.dispatchAll(); - inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), - eq(configRequest), eq(false), eq(true), eq(true), eq(false)); - mDut.onConfigSuccessResponse(transactionId.getValue()); - mMockLooper.dispatchAll(); - inOrder.verify(mMockCallback).onConnectSuccess(clientId); - inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(Process.myUid()), eq(false), - any()); - - inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(), - strCaptor.capture()); - collector.checkThat("interface created -- 0", sAwareInterfacePrefix + 0, - equalTo(strCaptor.getValue())); - mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0); - mMockLooper.dispatchAll(); + Messenger messenger = null; + if (isFirstIteration) { + messenger = initOobDataPathEndPoint(true, 1, clientId, inOrder, inOrderM); + } if (doPublish) { mDut.publish(clientId, publishConfig, mMockSessionCallback); @@ -1084,7 +1340,72 @@ public class WifiAwareDataPathStateManagerTest { eq(someMsg.getBytes())); return new DataPathEndPointInfo(sessionId.getValue(), peerIdCaptor.getValue(), - messengerCaptor.getValue()); + isFirstIteration ? messenger : null); + } + + private Messenger initOobDataPathEndPoint(boolean startUpSequence, int maxNdiInterfaces, + int clientId, InOrder inOrder, InOrder inOrderM) throws Exception { + final int pid = 2000; + final String callingPackage = "com.android.somePackage"; + final ConfigRequest configRequest = new ConfigRequest.Builder().build(); + + ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); + ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class); + ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class); + + Capabilities capabilities = new Capabilities(); + capabilities.maxNdiInterfaces = maxNdiInterfaces; + + if (startUpSequence) { + // (0) start/registrations + inOrder.verify(mMockCm).registerNetworkFactory(messengerCaptor.capture(), + strCaptor.capture()); + collector.checkThat("factory name", "WIFI_AWARE_FACTORY", + equalTo(strCaptor.getValue())); + + // (1) get capabilities + mDut.queryCapabilities(); + mMockLooper.dispatchAll(); + inOrder.verify(mMockNative).getCapabilities(transactionId.capture()); + mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), capabilities); + mMockLooper.dispatchAll(); + + // (2) enable usage + mDut.enableUsage(); + mMockLooper.dispatchAll(); + inOrderM.verify(mAwareMetricsMock).recordEnableUsage(); + } + + // (3) create client + mDut.connect(clientId, Process.myUid(), pid, callingPackage, mMockCallback, + configRequest, + false); + mMockLooper.dispatchAll(); + + if (startUpSequence) { + inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), + eq(configRequest), eq(false), eq(true), eq(true), eq(false)); + mDut.onConfigSuccessResponse(transactionId.getValue()); + mMockLooper.dispatchAll(); + } + + inOrder.verify(mMockCallback).onConnectSuccess(clientId); + inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(Process.myUid()), eq(false), + any()); + + if (startUpSequence) { + for (int i = 0; i < maxNdiInterfaces; ++i) { + inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(), + strCaptor.capture()); + collector.checkThat("interface created -- " + i, sAwareInterfacePrefix + i, + equalTo(strCaptor.getValue())); + mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0); + mMockLooper.dispatchAll(); + } + return messengerCaptor.getValue(); + } + + return null; } private static class DataPathEndPointInfo { diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java index 8cd1e1097..4252cfe6a 100644 --- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java +++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java @@ -655,8 +655,7 @@ public class WifiAwareMetricsTest { WifiAwareDataPathStateManager.AwareNetworkRequestInformation anri = new WifiAwareDataPathStateManager.AwareNetworkRequestInformation(); anri.networkSpecifier = ns; - anri.state = WifiAwareDataPathStateManager.AwareNetworkRequestInformation - .STATE_RESPONDER_CONFIRMED; + anri.state = WifiAwareDataPathStateManager.AwareNetworkRequestInformation.STATE_CONFIRMED; anri.uid = uid; anri.interfaceName = interfaceName; diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareRttStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareRttStateManagerTest.java index 616e68c0c..3df62f35d 100644 --- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareRttStateManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareRttStateManagerTest.java @@ -74,7 +74,7 @@ public class WifiAwareRttStateManagerTest { mTestLooper = new TestLooper(); BidirectionalAsyncChannelServer server = new BidirectionalAsyncChannelServer( mMockContext, mTestLooper.getLooper(), mMockHandler); - when(mMockRttService.getMessenger()).thenReturn(server.getMessenger()); + when(mMockRttService.getMessenger(null, new int[1])).thenReturn(server.getMessenger()); mDut.startWithRttService(mMockContext, mTestLooper.getLooper(), mMockRttService); } diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/LegacyPasspointConfigParserTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/LegacyPasspointConfigParserTest.java deleted file mode 100644 index 10ebceb22..000000000 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/LegacyPasspointConfigParserTest.java +++ /dev/null @@ -1,204 +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.hotspot2; - -import static org.junit.Assert.*; - -import android.os.FileUtils; -import android.test.suitebuilder.annotation.SmallTest; - -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -/** - * Unit tests for {@link com.android.server.wifi.hotspot2.LegacyPasspointConfigParser}. - */ -@SmallTest -public class LegacyPasspointConfigParserTest { - private static final String TEST_CONFIG = - "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n" - + "8:MgmtTree+\n" - + "17:PerProviderSubscription+\n" - + "4:r1i1+\n" - + "6:HomeSP+\n" - + "c:FriendlyName=12:Test Provider 1™\n" - + "4:FQDN=9:test1.net\n" - + "13:RoamingConsortiumOI=9:1234,5678\n" - + ".\n" - + "a:Credential+\n" - + "10:UsernamePassword+\n" - + "8:Username=5:user1\n" - + "8:Password=5:pass1\n" - + "\n" - + "9:EAPMethod+\n" - + "7:EAPType=2:21\n" - + "b:InnerMethod=3:PAP\n" - + ".\n" - + ".\n" - + "5:Realm=9:test1.com\n" - + ".\n" - + ".\n" - + ".\n" - + "17:PerProviderSubscription+\n" - + "4:r1i2+\n" - + "6:HomeSP+\n" - + "c:FriendlyName=f:Test Provider 2\n" - + "4:FQDN=9:test2.net\n" - + ".\n" - + "a:Credential+\n" - + "3:SIM+\n" - + "4:IMSI=4:1234\n" - + "7:EAPType=2:18\n" - + ".\n" - + "5:Realm=9:test2.com\n" - + ".\n" - + ".\n" - + ".\n" - + ".\n"; - - /** - * Helper function for generating {@link LegacyPasspointConfig} objects based on the predefined - * test configuration string {@link #TEST_CONFIG} - * - * @return Map of FQDN to {@link LegacyPasspointConfig} - */ - private Map<String, LegacyPasspointConfig> generateTestConfig() { - Map<String, LegacyPasspointConfig> configs = new HashMap<>(); - - LegacyPasspointConfig config1 = new LegacyPasspointConfig(); - config1.mFqdn = "test1.net"; - config1.mFriendlyName = "Test Provider 1™"; - config1.mRoamingConsortiumOis = new long[] {0x1234, 0x5678}; - config1.mRealm = "test1.com"; - configs.put("test1.net", config1); - - LegacyPasspointConfig config2 = new LegacyPasspointConfig(); - config2.mFqdn = "test2.net"; - config2.mFriendlyName = "Test Provider 2"; - config2.mRealm = "test2.com"; - config2.mImsi = "1234"; - configs.put("test2.net", config2); - - return configs; - } - - /** - * Helper function for parsing configuration data. - * - * @param data The configuration data to parse - * @return Map of FQDN to {@link LegacyPasspointConfig} - * @throws Exception - */ - private Map<String, LegacyPasspointConfig> parseConfig(String data) throws Exception { - // Write configuration data to file. - File configFile = File.createTempFile("LegacyPasspointConfig", ""); - FileUtils.stringToFile(configFile, data); - - // Parse the configuration file. - LegacyPasspointConfigParser parser = new LegacyPasspointConfigParser(); - Map<String, LegacyPasspointConfig> configMap = - parser.parseConfig(configFile.getAbsolutePath()); - - configFile.delete(); - return configMap; - } - - /** - * Verify that the expected {@link LegacyPasspointConfig} objects are return when parsing - * predefined test configuration data {@link #TEST_CONFIG}. - * - * @throws Exception - */ - @Test - public void parseTestConfig() throws Exception { - Map<String, LegacyPasspointConfig> parsedConfig = parseConfig(TEST_CONFIG); - assertEquals(generateTestConfig(), parsedConfig); - } - - /** - * Verify that an empty map is return when parsing a configuration containing an empty - * configuration (MgmtTree). - * - * @throws Exception - */ - @Test - public void parseEmptyConfig() throws Exception { - String emptyConfig = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n" - + "8:MgmtTree+\n" - + ".\n"; - Map<String, LegacyPasspointConfig> parsedConfig = parseConfig(emptyConfig); - assertTrue(parsedConfig.isEmpty()); - } - - /** - * Verify that an empty map is return when parsing an empty configuration data. - * - * @throws Exception - */ - @Test - public void parseEmptyData() throws Exception { - Map<String, LegacyPasspointConfig> parsedConfig = parseConfig(""); - assertTrue(parsedConfig.isEmpty()); - } - - /** - * Verify that an IOException is thrown when parsing a configuration containing an unknown - * root name. The expected root name is "MgmtTree". - * - * @throws Exception - */ - @Test(expected = IOException.class) - public void parseConfigWithUnknownRootName() throws Exception { - String config = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n" - + "8:TestTest+\n" - + ".\n"; - parseConfig(config); - } - - /** - * Verify that an IOException is thrown when parsing a configuration containing a line with - * mismatched string length for the name. - * - * @throws Exception - */ - @Test(expected = IOException.class) - public void parseConfigWithMismatchedStringLengthInName() throws Exception { - String config = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n" - + "9:MgmtTree+\n" - + ".\n"; - parseConfig(config); - } - - /** - * Verify that an IOException is thrown when parsing a configuration containing a line with - * mismatched string length for the value. - * - * @throws Exception - */ - @Test(expected = IOException.class) - public void parseConfigWithMismatchedStringLengthInValue() throws Exception { - String config = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n" - + "8:MgmtTree+\n" - + "4:test=5:test\n" - + ".\n"; - parseConfig(config); - } -} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java index 9f61ca09c..7ddddd250 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java @@ -59,6 +59,8 @@ public class PasspointNetworkEvaluatorTest { private static final int TEST_NETWORK_ID = 1; private static final String TEST_SSID1 = "ssid1"; private static final String TEST_SSID2 = "ssid2"; + private static final String TEST_BSSID1 = "01:23:45:56:78:9a"; + private static final String TEST_BSSID2 = "23:12:34:90:81:12"; private static final String TEST_FQDN1 = "test1.com"; private static final String TEST_FQDN2 = "test2.com"; private static final WifiConfiguration TEST_CONFIG1 = generateWifiConfig(TEST_FQDN1); @@ -107,14 +109,18 @@ public class PasspointNetworkEvaluatorTest { * @param rssiLevel The RSSI level associated with the scan * @return {@link ScanDetail} */ - private static ScanDetail generateScanDetail(String ssid) { + private static ScanDetail generateScanDetail(String ssid, String bssid) { NetworkDetail networkDetail = mock(NetworkDetail.class); when(networkDetail.isInterworking()).thenReturn(true); when(networkDetail.getAnt()).thenReturn(NetworkDetail.Ant.FreePublic); ScanDetail scanDetail = mock(ScanDetail.class); + ScanResult scanResult = new ScanResult(); + scanResult.SSID = ssid; + scanResult.BSSID = bssid; when(scanDetail.getSSID()).thenReturn(ssid); - when(scanDetail.getScanResult()).thenReturn(new ScanResult()); + when(scanDetail.getBSSIDString()).thenReturn(bssid); + when(scanDetail.getScanResult()).thenReturn(scanResult); when(scanDetail.getNetworkDetail()).thenReturn(networkDetail); return scanDetail; } @@ -138,7 +144,8 @@ public class PasspointNetworkEvaluatorTest { @Test public void evaluateScansWithNoMatch() throws Exception { List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { - generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)}); + generateScanDetail(TEST_SSID1, TEST_BSSID1), + generateScanDetail(TEST_SSID2, TEST_BSSID2)}); List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>(); when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(null); assertEquals(null, mEvaluator.evaluateNetworks( @@ -177,7 +184,8 @@ public class PasspointNetworkEvaluatorTest { @Test public void evaluateScansWithNetworkMatchingHomeProvider() throws Exception { List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { - generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)}); + generateScanDetail(TEST_SSID1, TEST_BSSID1), + generateScanDetail(TEST_SSID2, TEST_BSSID2)}); // Setup matching providers for ScanDetail with TEST_SSID1. Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create( @@ -219,7 +227,8 @@ public class PasspointNetworkEvaluatorTest { @Test public void evaluateScansWithNetworkMatchingRoamingProvider() throws Exception { List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { - generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)}); + generateScanDetail(TEST_SSID1, TEST_BSSID1), + generateScanDetail(TEST_SSID2, TEST_BSSID2)}); // Setup matching providers for ScanDetail with TEST_SSID1. Pair<PasspointProvider, PasspointMatch> roamingProvider = Pair.create( @@ -261,7 +270,8 @@ public class PasspointNetworkEvaluatorTest { @Test public void evaluateScansWithHomeProviderNewtorkAndRoamingProviderNetwork() throws Exception { List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { - generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)}); + generateScanDetail(TEST_SSID1, TEST_BSSID1), + generateScanDetail(TEST_SSID2, TEST_BSSID2)}); // Setup matching providers for ScanDetail with TEST_SSID1. Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create( @@ -305,7 +315,8 @@ public class PasspointNetworkEvaluatorTest { @Test public void evaluateScansWithActiveNetworkMatchingHomeProvider() throws Exception { List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { - generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)}); + generateScanDetail(TEST_SSID1, TEST_BSSID1), + generateScanDetail(TEST_SSID2, TEST_BSSID2)}); // Setup matching providers for both ScanDetail. Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create( @@ -315,7 +326,7 @@ public class PasspointNetworkEvaluatorTest { WifiConfiguration currentNetwork = new WifiConfiguration(); currentNetwork.networkId = TEST_NETWORK_ID; currentNetwork.SSID = ScanResultUtil.createQuotedSSID(TEST_SSID2); - String currentBssid = "12:23:34:45:12:0F"; + String currentBssid = TEST_BSSID2; // Returning the same matching provider for both ScanDetail. List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>(); @@ -344,7 +355,7 @@ public class PasspointNetworkEvaluatorTest { public void evaluateScanMatchingSIMProviderWithoutSIMCard() throws Exception { // Setup ScanDetail and match providers. List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { - generateScanDetail(TEST_SSID1)}); + generateScanDetail(TEST_SSID1, TEST_BSSID1)}); PasspointProvider testProvider = mock(PasspointProvider.class); Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create( testProvider, PasspointMatch.HomeProvider); @@ -357,6 +368,45 @@ public class PasspointNetworkEvaluatorTest { scanDetails, null, null, false, false, connectableNetworks)); assertTrue(connectableNetworks.isEmpty()); verify(testProvider, never()).getWifiConfig(); + } + + /** + * Verify that when the current active network is matched, the scan info associated with + * the network is updated. + * + * @throws Exception + */ + @Test + public void evaluateScansMatchingActiveNetworkWithDifferentBSS() throws Exception { + List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { + generateScanDetail(TEST_SSID1, TEST_BSSID2)}); + // Setup matching provider. + Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create( + TEST_PROVIDER1, PasspointMatch.HomeProvider); + + // Setup currently connected network. + WifiConfiguration currentNetwork = new WifiConfiguration(); + currentNetwork.networkId = TEST_NETWORK_ID; + currentNetwork.SSID = ScanResultUtil.createQuotedSSID(TEST_SSID1); + String currentBssid = TEST_BSSID1; + + // Match the current connected network to a home provider. + List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>(); + when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(homeProvider); + WifiConfiguration config = mEvaluator.evaluateNetworks(scanDetails, currentNetwork, + currentBssid, true, false, connectableNetworks); + assertEquals(1, connectableNetworks.size()); + // Verify network candidate information is updated. + ArgumentCaptor<ScanResult> updatedCandidateScanResult = + ArgumentCaptor.forClass(ScanResult.class); + verify(mWifiConfigManager).setNetworkCandidateScanResult(eq(TEST_NETWORK_ID), + updatedCandidateScanResult.capture(), anyInt()); + assertEquals(TEST_BSSID2, updatedCandidateScanResult.getValue().BSSID); + ArgumentCaptor<ScanDetail> updatedCandidateScanDetail = + ArgumentCaptor.forClass(ScanDetail.class); + verify(mWifiConfigManager).updateScanDetailForNetwork(eq(TEST_NETWORK_ID), + updatedCandidateScanDetail.capture()); + assertEquals(TEST_BSSID2, updatedCandidateScanDetail.getValue().getBSSIDString()); } } diff --git a/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java index 4443bb1f6..8b853a470 100644 --- a/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java +++ b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java @@ -83,8 +83,8 @@ public class SupplicantP2pIfaceCallbackTest { */ @Test public void testOnDeviceFound_success() throws Exception { - byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 }; - String fakePrimaryDeviceTypeString = "010203"; + byte[] fakePrimaryDeviceTypeBytes = { 0x00, 0x01, 0x02, -1, 0x04, 0x05, 0x06, 0x07 }; + String fakePrimaryDeviceTypeString = "1-02FF0405-1543"; String fakeDeviceName = "test device name"; short fakeConfigMethods = 0x1234; byte fakeCapabilities = 123; @@ -131,8 +131,8 @@ public class SupplicantP2pIfaceCallbackTest { */ @Test public void testOnDeviceFound_invalidArguments() throws Exception { - byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 }; - String fakePrimaryDeviceTypeString = "010203"; + byte[] fakePrimaryDeviceTypeBytes = { 0x0, 0x01, 0x02, -1, 0x04, 0x05, 0x06, 0x07 }; + String fakePrimaryDeviceTypeString = "1-02FF0405-1543"; String fakeDeviceName = "test device name"; short fakeConfigMethods = 0x1234; byte fakeCapabilities = 123; diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java index c63a9a042..974cf6635 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java @@ -429,6 +429,7 @@ public class WificondPnoScannerTest { expectHwDisconnectedPnoScanStart(order, pnoSettings); // Setup scan results + when(mWifiNative.getPnoScanResults()).thenReturn(scanResults.getScanDetailArrayList()); when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList()); // Notify scan has finished @@ -451,6 +452,7 @@ public class WificondPnoScannerTest { order.verify(mWifiNative).scan(eq(expectedScanFreqs), any(Set.class)); + when(mWifiNative.getPnoScanResults()).thenReturn(scanResults.getScanDetailArrayList()); when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList()); // Notify scan has finished @@ -478,6 +480,7 @@ public class WificondPnoScannerTest { // Setup scan results when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList()); + when(mWifiNative.getPnoScanResults()).thenReturn(scanResults.getScanDetailArrayList()); // Notify scan has finished mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT); diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java index ed7c58298..d337cf1cf 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java @@ -32,6 +32,7 @@ import com.android.server.wifi.ScanDetail; import com.android.server.wifi.ScanResults; import com.android.server.wifi.WifiMonitor; import com.android.server.wifi.WifiNative; +import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; import org.junit.Before; import org.junit.Test; @@ -56,6 +57,39 @@ public class WificondScannerTest extends BaseWifiScannerImplTest { mLooper.getLooper(), mClock); } + /** + * Test that WificondScannerImpl will not issue a scan and report scan failure + * when there is no channel to scan for. + */ + @Test + public void singleScanNotIssuedIfNoAvailableChannels() { + // Use mocked ChannelHelper and ChannelCollection to simulate the scenario + // that no channel is available for this request. + ChannelHelper channelHelper = mock(ChannelHelper.class); + ChannelCollection channelCollection = mock(ChannelCollection.class); + when(channelHelper.createChannelCollection()).thenReturn(channelCollection); + when(channelCollection.isEmpty()).thenReturn(true); + + mScanner = new WificondScannerImpl(mContext, mWifiNative, mWifiMonitor, + channelHelper, mLooper.getLooper(), mClock); + + WifiNative.ScanSettings settings = new NativeScanSettingsBuilder() + .withBasePeriod(10000) // ms + .withMaxApPerScan(10) + .addBucketWithBand(10000 /* ms */, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, + WifiScanner.WIFI_BAND_5_GHZ) + .build(); + WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); + mScanner.startSingleScan(settings, eventHandler); + + mLooper.dispatchAll(); + + // No scan is issued to WifiNative. + verify(mWifiNative, never()).scan(any(), any(Set.class)); + // A scan failed event must be reported. + verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_FAILED); + } + @Test public void backgroundScanSuccessSingleBucket() { WifiNative.ScanSettings settings = new NativeScanSettingsBuilder() diff --git a/tests/wifitests/src/com/android/server/wifi/util/MatrixTest.java b/tests/wifitests/src/com/android/server/wifi/util/MatrixTest.java new file mode 100644 index 000000000..bfa107159 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/util/MatrixTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi.util; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.util.Random; + +/** + * Unit tests for {@link com.android.server.wifi.util.Matrix}. + */ +public class MatrixTest { + /** + * Test that both forms of constructor work + */ + @Test + public void testConstructors() throws Exception { + assertEquals(new Matrix(3, 2), new Matrix(2, new double[]{0, 0, 0, 0, 0, 0})); + } + + /** + * Test some degenerate cases + */ + @Test + public void testDegenerate() throws Exception { + Matrix m1 = new Matrix(0, 20); + Matrix m2 = new Matrix(20, 0); + assertEquals(m1, m2.transpose()); + } + + /** + * Test addition + */ + @Test + public void testAddition() throws Exception { + Matrix m1 = new Matrix(2, new double[]{1, 2, 3, 4}); + Matrix m2 = new Matrix(2, new double[]{10, 20, 30, 40}); + Matrix m3 = new Matrix(2, new double[]{11, 22, 33, 44}); + assertEquals(m3, m1.plus(m2)); + } + + /** + * Test subtraction. + */ + @Test + public void testSubtraction() throws Exception { + Matrix m1 = new Matrix(2, new double[]{1, 2, 3, 4}); + Matrix m2 = new Matrix(2, new double[]{10, 20, 30, 40}); + Matrix m3 = new Matrix(2, new double[]{11, 22, 33, 44}); + assertEquals(m1, m3.minus(m2)); + } + + /** + * Test multiplication. + */ + @Test + public void testMultiplication() throws Exception { + Matrix m1 = new Matrix(2, new double[]{1, 2, 3, 4}); + Matrix m2 = new Matrix(2, new double[]{-3, 3, 7, 1}); + Matrix m3 = new Matrix(2, new double[]{11, 5, 19, 13}); + assertEquals(m3, m1.dot(m2)); + } + + /** + * Test that matrix inverse works (non-singular case). + */ + @Test + public void testInverse() throws Exception { + Matrix i3 = new Matrix(3, new double[]{1, 0, 0, 0, 1, 0, 0, 0, 1}); + Matrix f = new Matrix(3, new double[]{100, 100, 100, 100, 100, 100, 100, 100, 100}); + Matrix m1 = new Matrix(3, new double[]{10, 1, -1, 2, 14, -1, 0, 2, 20}); + Matrix m2 = m1.inverse(); + Matrix m12 = m1.dot(m2); + Matrix m21 = m2.dot(m1); + // Add f here to wash out the roundoff errors + assertEquals(i3.plus(f), m12.plus(f)); + assertEquals(i3.plus(f), m21.plus(f)); + } + + /** + * Test that attempting to invert a singular matrix throws an exception. + * @throws Exception + */ + @Test(expected = ArithmeticException.class) + public void testSingularity() throws Exception { + Matrix m1 = new Matrix(3, new double[]{10, 1, -1, 0, 0, 0, 0, 0, 0}); + Matrix m2 = m1.inverse(); + } + + /** + * Test that a copy is equal to the original, and that hash codes match, + * and that string versions match. + */ + @Test + public void testCopy() throws Exception { + Random random = new Random(); + Matrix m1 = new Matrix(3, 4); + for (int i = 0; i < m1.mem.length; i++) { + m1.mem[i] = random.nextDouble(); + } + Matrix m2 = new Matrix(m1); + assertEquals(m1, m2); + assertEquals(m1.hashCode(), m2.hashCode()); + assertEquals(m1.toString(), m2.toString()); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java index 3f51c5a6a..367e6b34a 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java @@ -98,10 +98,25 @@ public class NativeUtilTest { } /** + * Test that conversion of ssid bytes to quoted UTF8 string ssid works. + */ + @Test + public void testUtf8SsidDecode() throws Exception { + assertEquals( + new ArrayList<>( + Arrays.asList((byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, + (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x41, (byte) 0x50, + (byte) 0xe3, (byte) 0x81, (byte) 0x8f, (byte) 0xe3, (byte) 0x81, + (byte) 0xa0, (byte) 0xe3, (byte) 0x81, (byte) 0x95, (byte) 0xe3, + (byte) 0x81, (byte) 0x84)), + NativeUtil.decodeSsid("\"AndroidAPください\"")); + } + + /** * Test that conversion of ssid bytes to hex string ssid works. */ @Test - public void testNonAsciiSsidDecode() throws Exception { + public void testNonSsidDecode() throws Exception { assertEquals( new ArrayList<>( Arrays.asList((byte) 0xf5, (byte) 0xe4, (byte) 0xab, (byte) 0x78, @@ -110,10 +125,10 @@ public class NativeUtilTest { } /** - * Test that conversion of ssid bytes to quoted ASCII string ssid works. + * Test that conversion of ssid bytes to quoted string ssid works. */ @Test - public void testAsciiSsidEncode() throws Exception { + public void testSsidEncode() throws Exception { assertEquals( "\"ssid_test123\"", NativeUtil.encodeSsid(new ArrayList<>( @@ -126,7 +141,7 @@ public class NativeUtilTest { * Test that conversion of byte array to hex string works when the byte array contains 0. */ @Test - public void testNullCharInAsciiSsidEncode() throws Exception { + public void testNullCharInSsidEncode() throws Exception { assertEquals( "007369645f74657374313233", NativeUtil.encodeSsid(new ArrayList<>( @@ -139,7 +154,7 @@ public class NativeUtilTest { * Test that conversion of byte array to hex string ssid works. */ @Test - public void testNonAsciiSsidEncode() throws Exception { + public void testNonSsidEncode() throws Exception { assertEquals( "f5e4ab78ab3432439a", NativeUtil.encodeSsid(new ArrayList<>( diff --git a/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java index 308e26776..7f5a66d0e 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java @@ -19,6 +19,7 @@ package com.android.server.wifi.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; @@ -27,12 +28,15 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.app.AppOpsManager; +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.net.NetworkScoreManager; +import android.net.NetworkScorerAppData; +import android.net.wifi.WifiConfiguration; import android.os.Build; import android.os.RemoteException; import android.os.UserHandle; @@ -41,6 +45,7 @@ import android.provider.Settings; import com.android.server.wifi.BinderUtil; import com.android.server.wifi.FakeWifiLog; +import com.android.server.wifi.FrameworkFacade; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiSettingsStore; @@ -72,8 +77,10 @@ public class WifiPermissionsUtilTest { @Mock private UserManager mMockUserManager; @Mock private WifiSettingsStore mMockWifiSettingsStore; @Mock private ContentResolver mMockContentResolver; - @Mock private NetworkScoreManager mNetworkScoreManager; - @Mock private WifiInjector mWifiInjector; + @Mock private NetworkScoreManager mMockNetworkScoreManager; + @Mock private WifiInjector mMockWifiInjector; + @Mock private FrameworkFacade mMockFrameworkFacade; + @Mock private WifiConfiguration mMockWifiConfig; @Spy private FakeWifiLog mWifiLog; private static final String TEST_PACKAGE_NAME = "com.google.somePackage"; @@ -101,6 +108,10 @@ public class WifiPermissionsUtilTest { private boolean mActiveNwScorer; private Answer<Integer> mReturnPermission; private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>(); + private String mUseOpenWifiPackage; + private NetworkScorerAppData mNetworkScorerAppData; + private boolean mGetActiveScorerThrowsSecurityException; + private boolean mConfigIsOpen; /** * Set up Mockito tests @@ -124,8 +135,8 @@ public class WifiPermissionsUtilTest { mUid = MANAGED_PROFILE_UID; // do not really care about this value setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); when(mMockPermissionsWrapper.getOverrideWifiConfigPermission(anyInt())) .thenReturn(PackageManager.PERMISSION_GRANTED); assertTrue(codeUnderTest.checkConfigOverridePermission(mUid)); @@ -139,8 +150,8 @@ public class WifiPermissionsUtilTest { mUid = OTHER_USER_UID; // do not really care about this value setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); when(mMockPermissionsWrapper.getOverrideWifiConfigPermission(anyInt())) .thenReturn(PackageManager.PERMISSION_DENIED); assertFalse(codeUnderTest.checkConfigOverridePermission(mUid)); @@ -154,8 +165,8 @@ public class WifiPermissionsUtilTest { mUid = OTHER_USER_UID; // do not really care about this value setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); doThrow(new RemoteException("Failed to check permissions for " + mUid)) .when(mMockPermissionsWrapper).getOverrideWifiConfigPermission(mUid); assertFalse(codeUnderTest.checkConfigOverridePermission(mUid)); @@ -179,8 +190,8 @@ public class WifiPermissionsUtilTest { mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -207,8 +218,8 @@ public class WifiPermissionsUtilTest { mMockUserInfo.id = mCallingUser; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -230,8 +241,8 @@ public class WifiPermissionsUtilTest { mPermissionsList.put(mMacAddressPermission, mUid); setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -259,8 +270,8 @@ public class WifiPermissionsUtilTest { mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid); setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -286,8 +297,8 @@ public class WifiPermissionsUtilTest { mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -312,8 +323,8 @@ public class WifiPermissionsUtilTest { mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -323,6 +334,354 @@ public class WifiPermissionsUtilTest { } /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User is current + * The current config is for an open network. + * Validate result is true + */ + @Test + public void testCanAccessFullConnectionInfo_PackageIsUseOpenWifiPackage() throws Exception { + final boolean output; + mThrowSecurityException = false; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + ComponentName useOpenWifiComponent = new ComponentName(TEST_PACKAGE_NAME, "TestClass"); + mNetworkScorerAppData = new NetworkScorerAppData(0 /*packageUid*/, + null /*recommendationServiceComp*/, null /*recommendationServiceLabel*/, + useOpenWifiComponent, null /*networkAvailableNotificationChannelId*/); + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(true, output); + } + + /** + * Test case setting: Package is valid because the caller has access to scan results. + * Location mode is ON + * User is current + * The current config is not for an open network. + * Validate result is true + */ + @Test + public void testCanAccessFullConnectionInfo_HasAccessToScanResults() throws Exception { + final boolean output; + mThrowSecurityException = false; + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.GINGERBREAD; + mLocationModeSetting = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY; + mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mConfigIsOpen = false; + + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(true, output); + } + + /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User or profile is not current but the uid has + * permission to INTERACT_ACROSS_USERS_FULL + * The current config is for an open network. + * Validate result is true + */ + @Test + public void testCanAccessFullConnectionInfo_UserNotCurrentButHasInteractAcrossUsers() + throws Exception { + final boolean output; + mThrowSecurityException = false; + mUid = MANAGED_PROFILE_UID; + mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid); + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + ComponentName useOpenWifiComponent = new ComponentName(TEST_PACKAGE_NAME, "TestClass"); + mNetworkScorerAppData = new NetworkScorerAppData(0 /*packageUid*/, + null /*recommendationServiceComp*/, null /*recommendationServiceLabel*/, + useOpenWifiComponent, null /*networkAvailableNotificationChannelId*/); + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(true, output); + } + + /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User or profile is NOT current + * INTERACT_ACROSS_USERS_FULL NOT granted + * The current config is for an open network. + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_UserNotCurrentNoInteractAcrossUsers() + throws Exception { + final boolean output; + mThrowSecurityException = false; + mUid = MANAGED_PROFILE_UID; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User is current + * The current config is NULL. + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_WiFiConfigIsNull() throws Exception { + final boolean output; + mThrowSecurityException = false; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + ComponentName useOpenWifiComponent = new ComponentName(TEST_PACKAGE_NAME, "TestClass"); + mNetworkScorerAppData = new NetworkScorerAppData(0 /*packageUid*/, + null /*recommendationServiceComp*/, null /*recommendationServiceLabel*/, + useOpenWifiComponent, null /*networkAvailableNotificationChannelId*/); + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(null /*config*/, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User is current + * The current config is not for an open network. + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_WiFiConfigIsNotOpen() throws Exception { + final boolean output; + mThrowSecurityException = false; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + ComponentName useOpenWifiComponent = new ComponentName(TEST_PACKAGE_NAME, "TestClass"); + mNetworkScorerAppData = new NetworkScorerAppData(0 /*packageUid*/, + null /*recommendationServiceComp*/, null /*recommendationServiceLabel*/, + useOpenWifiComponent, null /*networkAvailableNotificationChannelId*/); + mConfigIsOpen = false; + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User is current + * The current config is for an open network. + * There is no active scorer + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_UseOpenWifiPackageIsSetButNoActiveScorer() + throws Exception { + final boolean output; + mThrowSecurityException = false; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + mNetworkScorerAppData = null; // getActiveScorer() will return null + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User is current + * The current config is for an open network. + * The scorer is active but the useOpenWiFi component name doesn't match + * the provided package. + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_MismatchBetweenUseOpenWifiPackages() + throws Exception { + final boolean output; + mThrowSecurityException = false; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + ComponentName useOpenWifiComponent = + new ComponentName(mUseOpenWifiPackage + ".nomatch", "TestClass"); + mNetworkScorerAppData = new NetworkScorerAppData(0 /*packageUid*/, + null /*recommendationServiceComp*/, null /*recommendationServiceLabel*/, + useOpenWifiComponent, null /*networkAvailableNotificationChannelId*/); + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: Package is valid because it matches the USE_OPEN_WIFI_PACKAGE. + * User is current + * The current config is for an open network. + * The scorer is active but the useOpenWiFi component name is null. + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_UseOpenWifiPackageFromScorerIsNull() + throws Exception { + final boolean output; + mThrowSecurityException = false; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + mNetworkScorerAppData = new NetworkScorerAppData(0 /*packageUid*/, + null /*recommendationServiceComp*/, null /*recommendationServiceLabel*/, + null /*useOpenWifiComponent*/, null /*networkAvailableNotificationChannelId*/); + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: Package is invalid because USE_OPEN_WIFI_PACKAGE is an empty string. + * Location mode is ON + * User is current + * The current config is for an open network. + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_UseOpenWifiPackageIsEmpty() throws Exception { + final boolean output; + mThrowSecurityException = false; + mLocationModeSetting = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = ""; + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: Package is invalid because it does not match the USE_OPEN_WIFI_PACKAGE. + * User is current + * The current config is for an open network. + * Validate result is false + */ + @Test + public void testCanAccessFullConnectionInfo_DoesNotMatchUseOpenWifiPackage() throws Exception { + final boolean output; + mThrowSecurityException = false; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME + ".nomatch"; + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + output = codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, + mUid, mTargetVersion); + + assertEquals(false, output); + } + + /** + * Test case setting: The caller is invalid because its UID does not match the provided package. + * + * Validate a SecurityException is thrown. + */ + @Test + public void testCanAccessFullConnectionInfo_UidPackageCheckFails() throws Exception { + mThrowSecurityException = true; + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + try { + codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, mUid, + mTargetVersion); + fail("SecurityException not thrown."); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test case setting: The getActiveScorer() call fails with a SecurityException. + * + * Validate a SecurityException is thrown. + */ + @Test + public void testCanAccessFullConnectionInfo_GetActiveScorerFails() throws Exception { + mThrowSecurityException = false; + mGetActiveScorerThrowsSecurityException = true; + mLocationModeSetting = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY; + mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; + mUseOpenWifiPackage = TEST_PACKAGE_NAME; + setupTestCase(); + WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); + + try { + codeUnderTest.canAccessFullConnectionInfo(mMockWifiConfig, TEST_PACKAGE_NAME, mUid, + mTargetVersion); + fail("SecurityException not thrown."); + } catch (SecurityException e) { + // expected + } + } + + /** * Test case Setting: Package is valid * Legacy App * Foreground @@ -341,8 +700,8 @@ public class WifiPermissionsUtilTest { mCurrentUser = UserHandle.USER_CURRENT_OR_SELF; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -373,8 +732,8 @@ public class WifiPermissionsUtilTest { mMockUserInfo.id = mCallingUser; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -400,8 +759,8 @@ public class WifiPermissionsUtilTest { mLocationModeSetting = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -419,8 +778,8 @@ public class WifiPermissionsUtilTest { boolean output = false; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); try { output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion); } catch (SecurityException e) { @@ -444,8 +803,8 @@ public class WifiPermissionsUtilTest { mMockUserInfo.id = mCallingUser; setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); codeUnderTest.enforceLocationPermission(TEST_PACKAGE_NAME, mUid); } @@ -457,8 +816,8 @@ public class WifiPermissionsUtilTest { public void testEnforceLocationPermissionExpectSecurityException() throws Exception { setupTestCase(); WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper, - mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager, - mWifiInjector); + mMockContext, mMockWifiSettingsStore, mMockUserManager, mMockNetworkScoreManager, + mMockWifiInjector); codeUnderTest.enforceLocationPermission(TEST_PACKAGE_NAME, mUid); } @@ -500,7 +859,15 @@ public class WifiPermissionsUtilTest { when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); when(mMockContext.getSystemService(Context.USER_SERVICE)) .thenReturn(mMockUserManager); - when(mWifiInjector.makeLog(anyString())).thenReturn(mWifiLog); + when(mMockWifiInjector.makeLog(anyString())).thenReturn(mWifiLog); + when(mMockWifiInjector.getFrameworkFacade()).thenReturn(mMockFrameworkFacade); + if (mGetActiveScorerThrowsSecurityException) { + when(mMockNetworkScoreManager.getActiveScorer()).thenThrow( + new SecurityException("Caller is neither the system process nor a " + + "score requester.")); + } else { + when(mMockNetworkScoreManager.getActiveScorer()).thenReturn(mNetworkScorerAppData); + } } private void initTestVars() { @@ -518,6 +885,10 @@ public class WifiPermissionsUtilTest { mCoarseLocationPermission = PackageManager.PERMISSION_DENIED; mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED; mActiveNwScorer = false; + mUseOpenWifiPackage = null; + mNetworkScorerAppData = null; + mGetActiveScorerThrowsSecurityException = false; + mConfigIsOpen = true; } private void setupMockInterface() { @@ -528,11 +899,14 @@ public class WifiPermissionsUtilTest { anyString(), anyInt()); when(mMockPermissionsWrapper.getCallingUserId(mUid)).thenReturn(mCallingUser); when(mMockPermissionsWrapper.getCurrentUser()).thenReturn(mCurrentUser); - when(mNetworkScoreManager.isCallerActiveScorer(mUid)).thenReturn(mActiveNwScorer); + when(mMockNetworkScoreManager.isCallerActiveScorer(mUid)).thenReturn(mActiveNwScorer); when(mMockPermissionsWrapper.getUidPermission(mManifestStringCoarse, mUid)) .thenReturn(mCoarseLocationPermission); when(mMockWifiSettingsStore.getLocationModeSetting(mMockContext)) .thenReturn(mLocationModeSetting); when(mMockPermissionsWrapper.getTopPkgName()).thenReturn(mPkgNameOfTopActivity); + when(mMockFrameworkFacade.getStringSetting(mMockContext, + Settings.Global.USE_OPEN_WIFI_PACKAGE)).thenReturn(mUseOpenWifiPackage); + when(mMockWifiConfig.isOpenNetwork()).thenReturn(mConfigIsOpen); } } |