Merge "[Wi-Fi] Create WifiNetworkDetailsFragment related version 2 files for WifiTracker2 development"
diff --git a/res/xml/wifi_display_saved_access_points2.xml b/res/xml/wifi_display_saved_access_points2.xml
new file mode 100644
index 0000000..02c732a
--- /dev/null
+++ b/res/xml/wifi_display_saved_access_points2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="saved_access_points"
+ android:title="@string/wifi_saved_access_points_label">
+
+ <PreferenceCategory
+ android:key="subscribed_access_points_category"
+ android:title="@string/wifi_subscribed_access_points_tab"
+ settings:controller="com.android.settings.wifi.savedaccesspoints2.SubscribedAccessPointsPreferenceController2"/>
+
+ <PreferenceCategory
+ android:key="saved_access_points_category"
+ android:title="@string/wifi_saved_access_points_tab"
+ settings:controller="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsPreferenceController2"/>
+
+</PreferenceScreen>
diff --git a/res/xml/wifi_settings2.xml b/res/xml/wifi_settings2.xml
new file mode 100644
index 0000000..8cd3857
--- /dev/null
+++ b/res/xml/wifi_settings2.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/wifi_settings"
+ settings:keywords="@string/keywords_wifi">
+
+ <com.android.settings.wifi.LinkablePreference
+ android:key="wifi_status_message"/>
+
+ <PreferenceCategory
+ android:key="connected_access_point"
+ android:layout="@layout/preference_category_no_label"/>
+
+ <PreferenceCategory
+ android:key="access_points"
+ android:layout="@layout/preference_category_no_label"/>
+
+ <Preference
+ android:key="configure_settings"
+ android:title="@string/wifi_configure_settings_preference_title"
+ settings:allowDividerAbove="true"
+ android:fragment="com.android.settings.wifi.ConfigureWifiSettings"/>
+
+ <Preference
+ android:key="saved_networks"
+ android:title="@string/wifi_saved_access_points_label"
+ android:fragment="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2"/>
+
+ <com.android.settings.datausage.DataUsagePreference
+ android:key="wifi_data_usage"
+ android:title="@string/wifi_data_usage"/>
+</PreferenceScreen>
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 0934ba9..98c6b87 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -146,6 +146,7 @@
import com.android.settings.wifi.calling.WifiCallingSettings;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
+import com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2;
import com.android.settings.wifi.tether.WifiTetherSettings;
public class SettingsGateway {
@@ -161,6 +162,7 @@
WifiSettings2.class.getName(),
ConfigureWifiSettings.class.getName(),
SavedAccessPointsWifiSettings.class.getName(),
+ SavedAccessPointsWifiSettings2.class.getName(),
TetherSettings.class.getName(),
WifiP2pSettings.class.getName(),
WifiTetherSettings.class.getName(),
diff --git a/src/com/android/settings/panel/WifiPanel.java b/src/com/android/settings/panel/WifiPanel.java
index 36ee117..0efa804 100644
--- a/src/com/android/settings/panel/WifiPanel.java
+++ b/src/com/android/settings/panel/WifiPanel.java
@@ -20,12 +20,14 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.util.FeatureFlagUtils;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.WifiSettings;
+import com.android.settings.wifi.WifiSettings2;
import java.util.ArrayList;
import java.util.List;
@@ -61,11 +63,20 @@
public Intent getSeeMoreIntent() {
final String screenTitle =
mContext.getText(R.string.wifi_settings).toString();
- final Intent intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
- WifiSettings.class.getName(),
- null /* key */,
- screenTitle,
- SettingsEnums.WIFI);
+ Intent intent;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+ WifiSettings2.class.getName(),
+ null /* key */,
+ screenTitle,
+ SettingsEnums.WIFI);
+ } else {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+ WifiSettings.class.getName(),
+ null /* key */,
+ screenTitle,
+ SettingsEnums.WIFI);
+ }
intent.setClassName(mContext.getPackageName(), SubSettings.class.getName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 0ba2a94..a3435e6 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -44,6 +44,7 @@
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
@@ -70,6 +71,7 @@
import com.android.settings.ProxySelector;
import com.android.settings.R;
import com.android.settings.wifi.details.WifiPrivacyPreferenceController;
+import com.android.settings.wifi.details2.WifiPrivacyPreferenceController2;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settingslib.Utils;
import com.android.settingslib.utils.ThreadUtils;
@@ -289,9 +291,14 @@
? HIDDEN_NETWORK
: NOT_HIDDEN_NETWORK);
- final int prefMacValue =
- WifiPrivacyPreferenceController.translateMacRandomizedValueToPrefValue(
- config.macRandomizationSetting);
+ int prefMacValue;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ prefMacValue = WifiPrivacyPreferenceController2
+ .translateMacRandomizedValueToPrefValue(config.macRandomizationSetting);
+ } else {
+ prefMacValue = WifiPrivacyPreferenceController
+ .translateMacRandomizedValueToPrefValue(config.macRandomizationSetting);
+ }
mPrivacySettingsSpinner.setSelection(prefMacValue);
if (config.getIpAssignment() == IpAssignment.STATIC) {
@@ -843,9 +850,14 @@
}
if (mPrivacySettingsSpinner != null) {
- final int macValue =
- WifiPrivacyPreferenceController.translatePrefValueToMacRandomizedValue(
- mPrivacySettingsSpinner.getSelectedItemPosition());
+ int macValue;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ macValue = WifiPrivacyPreferenceController2.translatePrefValueToMacRandomizedValue(
+ mPrivacySettingsSpinner.getSelectedItemPosition());
+ } else {
+ macValue = WifiPrivacyPreferenceController.translatePrefValueToMacRandomizedValue(
+ mPrivacySettingsSpinner.getSelectedItemPosition());
+ }
config.macRandomizationSetting = macValue;
}
diff --git a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
index 36c4455..2c6feac 100644
--- a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
+++ b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.os.Bundle;
+import android.util.FeatureFlagUtils;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
@@ -25,6 +26,7 @@
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.wifi.AccessPoint;
@@ -128,17 +130,31 @@
mPreference.refresh();
mPreference.setOrder(order);
- mPreference.setOnPreferenceClickListener(pref -> {
- Bundle args = new Bundle();
- mPreference.getAccessPoint().saveWifiState(args);
- new SubSettingLauncher(mPrefContext)
- .setTitleRes(R.string.pref_title_network_details)
- .setDestination(WifiNetworkDetailsFragment.class.getName())
- .setArguments(args)
- .setSourceMetricsCategory(mMetricsCategory)
- .launch();
- return true;
- });
+ if (FeatureFlagUtils.isEnabled(mPrefContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ mPreference.setOnPreferenceClickListener(pref -> {
+ Bundle args = new Bundle();
+ mPreference.getAccessPoint().saveWifiState(args);
+ new SubSettingLauncher(mPrefContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsCategory)
+ .launch();
+ return true;
+ });
+ } else {
+ mPreference.setOnPreferenceClickListener(pref -> {
+ Bundle args = new Bundle();
+ mPreference.getAccessPoint().saveWifiState(args);
+ new SubSettingLauncher(mPrefContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment.class.getName())
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsCategory)
+ .launch();
+ return true;
+ });
+ }
mPreferenceGroup.addPreference(mPreference);
}
}
diff --git a/src/com/android/settings/wifi/WifiPickerActivity.java b/src/com/android/settings/wifi/WifiPickerActivity.java
index a590a0f..adfc7ec 100644
--- a/src/com/android/settings/wifi/WifiPickerActivity.java
+++ b/src/com/android/settings/wifi/WifiPickerActivity.java
@@ -16,6 +16,7 @@
package com.android.settings.wifi;
import android.content.Intent;
+import android.util.FeatureFlagUtils;
import androidx.preference.PreferenceFragmentCompat;
@@ -24,6 +25,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
+import com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2;
public class WifiPickerActivity extends SettingsActivity implements ButtonBarHandler {
@@ -39,9 +41,18 @@
@Override
protected boolean isValidFragment(String fragmentName) {
+ boolean isSavedAccessPointsWifiSettings;
+ if (FeatureFlagUtils.isEnabled(this, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ isSavedAccessPointsWifiSettings =
+ SavedAccessPointsWifiSettings2.class.getName().equals(fragmentName);
+ } else {
+ isSavedAccessPointsWifiSettings =
+ SavedAccessPointsWifiSettings.class.getName().equals(fragmentName);
+ }
+
if (WifiSettings.class.getName().equals(fragmentName)
|| WifiP2pSettings.class.getName().equals(fragmentName)
- || SavedAccessPointsWifiSettings.class.getName().equals(fragmentName)) {
+ || isSavedAccessPointsWifiSettings) {
return true;
}
return false;
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 28b668f..a5b380e 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -68,6 +68,7 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SwitchBarController;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -954,12 +955,21 @@
? accessPoint.getTitle()
: context.getText(R.string.pref_title_network_details);
- new SubSettingLauncher(getContext())
- .setTitleText(title)
- .setDestination(WifiNetworkDetailsFragment.class.getName())
- .setArguments(pref.getExtras())
- .setSourceMetricsCategory(getMetricsCategory())
- .launch();
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ new SubSettingLauncher(getContext())
+ .setTitleText(title)
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(pref.getExtras())
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ } else {
+ new SubSettingLauncher(getContext())
+ .setTitleText(title)
+ .setDestination(WifiNetworkDetailsFragment.class.getName())
+ .setArguments(pref.getExtras())
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ }
}
private Network getCurrentWifiNetwork() {
diff --git a/src/com/android/settings/wifi/WifiSettings2.java b/src/com/android/settings/wifi/WifiSettings2.java
index 2d26cc4..f2cd1cf 100644
--- a/src/com/android/settings/wifi/WifiSettings2.java
+++ b/src/com/android/settings/wifi/WifiSettings2.java
@@ -190,7 +190,7 @@
}
private void addPreferences() {
- addPreferencesFromResource(R.xml.wifi_settings);
+ addPreferencesFromResource(R.xml.wifi_settings2);
mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS);
mWifiEntryPreferenceCategory = findPreference(PREF_KEY_ACCESS_POINTS);
diff --git a/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
new file mode 100644
index 0000000..de831b7
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settingslib.wifi.AccessPoint;
+
+/**
+ * {@link BasePreferenceController} that launches Wi-Fi Easy Connect configurator flow
+ */
+public class AddDevicePreferenceController2 extends BasePreferenceController {
+
+ private static final String TAG = "AddDevicePreferenceController2";
+
+ private static final String KEY_ADD_DEVICE = "add_device_to_network";
+
+ private AccessPoint mAccessPoint;
+ private WifiManager mWifiManager;
+
+ public AddDevicePreferenceController2(Context context) {
+ super(context, KEY_ADD_DEVICE);
+
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ /**
+ * Initiate with an {@link AccessPoint}.
+ */
+ public AddDevicePreferenceController2 init(AccessPoint accessPoint) {
+ mAccessPoint = accessPoint;
+
+ return this;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (WifiDppUtils.isSupportConfiguratorQrCodeScanner(mContext, mAccessPoint)) {
+ return AVAILABLE;
+ } else {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (KEY_ADD_DEVICE.equals(preference.getKey())) {
+ WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorQrCodeScanner());
+ return true; /* click is handled */
+ }
+
+ return false; /* click is not handled */
+ }
+
+ private void launchWifiDppConfiguratorQrCodeScanner() {
+ final Intent intent = WifiDppUtils.getConfiguratorQrCodeScannerIntentOrNull(mContext,
+ mWifiManager, mAccessPoint);
+
+ if (intent == null) {
+ Log.e(TAG, "Launch Wi-Fi QR code scanner with a wrong Wi-Fi network!");
+ } else {
+ mContext.startActivity(intent);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
new file mode 100644
index 0000000..1d6e457
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -0,0 +1,1207 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.core.text.BidiFormatter;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.FeatureFlags;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.datausage.WifiDataUsageSummaryPreferenceController;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settings.wifi.WifiDialog.WifiDialogListener;
+import com.android.settings.wifi.WifiUtils;
+import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.widget.ActionButtonsPreference;
+import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.time.Duration;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+/**
+ * Controller for logic pertaining to displaying Wifi information for the
+ * {@link WifiNetworkDetailsFragment}.
+ */
+public class WifiDetailPreferenceController2 extends AbstractPreferenceController
+ implements PreferenceControllerMixin, WifiDialogListener, LifecycleObserver, OnPause,
+ OnResume {
+
+ private static final String TAG = "WifiDetailsPrefCtrl2";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @VisibleForTesting
+ static final String KEY_HEADER = "connection_header";
+ @VisibleForTesting
+ static final String KEY_DATA_USAGE_HEADER = "status_header";
+ @VisibleForTesting
+ static final String KEY_BUTTONS_PREF = "buttons";
+ @VisibleForTesting
+ static final String KEY_SIGNAL_STRENGTH_PREF = "signal_strength";
+ @VisibleForTesting
+ static final String KEY_TX_LINK_SPEED = "tx_link_speed";
+ @VisibleForTesting
+ static final String KEY_RX_LINK_SPEED = "rx_link_speed";
+ @VisibleForTesting
+ static final String KEY_FREQUENCY_PREF = "frequency";
+ @VisibleForTesting
+ static final String KEY_SECURITY_PREF = "security";
+ @VisibleForTesting
+ static final String KEY_SSID_PREF = "ssid";
+ @VisibleForTesting
+ static final String KEY_MAC_ADDRESS_PREF = "mac_address";
+ @VisibleForTesting
+ static final String KEY_IP_ADDRESS_PREF = "ip_address";
+ @VisibleForTesting
+ static final String KEY_GATEWAY_PREF = "gateway";
+ @VisibleForTesting
+ static final String KEY_SUBNET_MASK_PREF = "subnet_mask";
+ @VisibleForTesting
+ static final String KEY_DNS_PREF = "dns";
+ @VisibleForTesting
+ static final String KEY_IPV6_CATEGORY = "ipv6_category";
+ @VisibleForTesting
+ static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses";
+
+ private static final int STATE_NONE = 1;
+ private static final int STATE_ENABLE_WIFI = 2;
+ private static final int STATE_ENABLE_WIFI_FAILED = 3;
+ private static final int STATE_CONNECTING = 4;
+ private static final int STATE_CONNECTED = 5;
+ private static final int STATE_FAILED = 6;
+ private static final int STATE_NOT_IN_RANGE = 7;
+ private static final int STATE_DISCONNECTED = 8;
+ private static final long TIMEOUT = Duration.ofSeconds(10).toMillis();
+
+ // Be static to avoid too much object not be reset.
+ @VisibleForTesting
+ static CountDownTimer sTimer;
+
+ private AccessPoint mAccessPoint;
+ private final ConnectivityManager mConnectivityManager;
+ private final PreferenceFragmentCompat mFragment;
+ private final Handler mHandler;
+ private LinkProperties mLinkProperties;
+ private Network mNetwork;
+ private NetworkInfo mNetworkInfo;
+ private NetworkCapabilities mNetworkCapabilities;
+ private int mRssiSignalLevel = -1;
+ private String[] mSignalStr;
+ private WifiConfiguration mWifiConfig;
+ private WifiInfo mWifiInfo;
+ private final WifiManager mWifiManager;
+ private final WifiTracker mWifiTracker;
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+ private boolean mIsOutOfRange;
+ private boolean mIsEphemeral;
+ private boolean mConnected;
+ private int mConnectingState;
+ private WifiManager.ActionListener mConnectListener;
+
+ // UI elements - in order of appearance
+ private ActionButtonsPreference mButtonsPref;
+ private EntityHeaderController mEntityHeaderController;
+ private Preference mSignalStrengthPref;
+ private Preference mTxLinkSpeedPref;
+ private Preference mRxLinkSpeedPref;
+ private Preference mFrequencyPref;
+ private Preference mSecurityPref;
+ private Preference mSsidPref;
+ private Preference mMacAddressPref;
+ private Preference mIpAddressPref;
+ private Preference mGatewayPref;
+ private Preference mSubnetPref;
+ private Preference mDnsPref;
+ private PreferenceCategory mIpv6Category;
+ private Preference mIpv6AddressPref;
+ private Lifecycle mLifecycle;
+ Preference mDataUsageSummaryPref;
+ WifiDataUsageSummaryPreferenceController mSummaryHeaderController;
+
+ private final IconInjector mIconInjector;
+ private final IntentFilter mFilter;
+
+ // Passpoint information - cache it in case of losing these information after
+ // updateAccessPointFromScannedList(). For R2, we should update these data from
+ // WifiManager#getPasspointConfigurations() after users manage the passpoint profile.
+ private boolean mIsExpired;
+ private boolean mIsPasspointConfigurationR1;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION:
+ if (!intent.getBooleanExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED,
+ false /* defaultValue */)) {
+ // only one network changed
+ WifiConfiguration wifiConfiguration = intent
+ .getParcelableExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
+ if (mAccessPoint.matches(wifiConfiguration)) {
+ mWifiConfig = wifiConfiguration;
+ }
+ }
+ // fall through
+ case WifiManager.NETWORK_STATE_CHANGED_ACTION:
+ case WifiManager.RSSI_CHANGED_ACTION:
+ refreshPage();
+ break;
+ }
+ }
+ };
+
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+ .clearCapabilities().addTransportType(TRANSPORT_WIFI).build();
+
+ // Must be run on the UI thread since it directly manipulates UI state.
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+ if (network.equals(mNetwork) && !lp.equals(mLinkProperties)) {
+ mLinkProperties = lp;
+ refreshIpLayerInfo();
+ }
+ }
+
+ private boolean hasCapabilityChanged(NetworkCapabilities nc, int cap) {
+ // If this is the first time we get NetworkCapabilities, report that something changed.
+ if (mNetworkCapabilities == null) return true;
+
+ // nc can never be null, see ConnectivityService#callCallbackForRequest.
+ return mNetworkCapabilities.hasCapability(cap) != nc.hasCapability(cap);
+ }
+
+ private boolean hasPrivateDnsStatusChanged(NetworkCapabilities nc) {
+ // If this is the first time that WifiDetailPreferenceController2 gets
+ // NetworkCapabilities, report that something has changed and assign nc to
+ // mNetworkCapabilities in onCapabilitiesChanged. Note that the NetworkCapabilities
+ // from onCapabilitiesChanged() will never be null, so calling
+ // mNetworkCapabilities.isPrivateDnsBroken() would be safe next time.
+ if (mNetworkCapabilities == null) {
+ return true;
+ }
+
+ return mNetworkCapabilities.isPrivateDnsBroken() != nc.isPrivateDnsBroken();
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ // If the network just validated or lost Internet access or detected partial internet
+ // connectivity or private dns was broken, refresh network state. Don't do this on
+ // every NetworkCapabilities change because refreshEntityHeader sends IPCs to the
+ // system server from the UI thread, which can cause jank.
+ if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
+ if (hasPrivateDnsStatusChanged(nc)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
+ mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ refreshEntityHeader();
+ }
+ mNetworkCapabilities = nc;
+ refreshButtons();
+ refreshIpLayerInfo();
+ }
+ }
+
+ @Override
+ public void onLost(Network network) {
+ // Ephemeral network not a saved network, leave detail page once disconnected
+ if (mIsEphemeral && network.equals(mNetwork)) {
+ exitActivity();
+ }
+ }
+ };
+
+ @VisibleForTesting
+ final WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() {
+ /** Called when the state of Wifi has changed. */
+ public void onWifiStateChanged(int state) {
+ Log.d(TAG, "onWifiStateChanged(" + state + ")");
+ if (mConnectingState == STATE_ENABLE_WIFI && state == WifiManager.WIFI_STATE_ENABLED) {
+ updateConnectingState(STATE_CONNECTING);
+ } else if (mConnectingState != STATE_NONE && state == WifiManager.WIFI_STATE_DISABLED) {
+ // update as disconnected once Wi-Fi disabled since may not received
+ // onConnectedChanged for this case.
+ updateConnectingState(STATE_DISCONNECTED);
+ }
+ }
+
+ /** Called when the connection state of wifi has changed. */
+ public void onConnectedChanged() {
+ refreshPage();
+ }
+
+ /**
+ * Called to indicate the list of AccessPoints has been updated and
+ * {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
+ */
+ public void onAccessPointsChanged() {
+ refreshPage();
+ }
+ };
+
+ /**
+ * To get an instance of {@link WifiDetailPreferenceController2}
+ */
+ public static WifiDetailPreferenceController2 newInstance(
+ AccessPoint accessPoint,
+ ConnectivityManager connectivityManager,
+ Context context,
+ PreferenceFragmentCompat fragment,
+ Handler handler,
+ Lifecycle lifecycle,
+ WifiManager wifiManager,
+ MetricsFeatureProvider metricsFeatureProvider) {
+ return new WifiDetailPreferenceController2(
+ accessPoint, connectivityManager, context, fragment, handler, lifecycle,
+ wifiManager, metricsFeatureProvider, new IconInjector(context));
+ }
+
+ @VisibleForTesting
+ /* package */ WifiDetailPreferenceController2(
+ AccessPoint accessPoint,
+ ConnectivityManager connectivityManager,
+ Context context,
+ PreferenceFragmentCompat fragment,
+ Handler handler,
+ Lifecycle lifecycle,
+ WifiManager wifiManager,
+ MetricsFeatureProvider metricsFeatureProvider,
+ IconInjector injector) {
+ super(context);
+
+ mAccessPoint = accessPoint;
+ mConnectivityManager = connectivityManager;
+ mFragment = fragment;
+ mHandler = handler;
+ mSignalStr = context.getResources().getStringArray(R.array.wifi_signal);
+ mWifiConfig = accessPoint.getConfig();
+ mWifiManager = wifiManager;
+ mMetricsFeatureProvider = metricsFeatureProvider;
+ mIconInjector = injector;
+
+ mFilter = new IntentFilter();
+ mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+
+ mLifecycle = lifecycle;
+ lifecycle.addObserver(this);
+
+ mWifiTracker = WifiTrackerFactory.create(
+ mFragment.getActivity(),
+ mWifiListener,
+ mLifecycle,
+ true /*includeSaved*/,
+ true /*includeScans*/);
+ mConnected = mAccessPoint.isActive();
+ // When lost the network connection, WifiInfo/NetworkInfo will be clear. So causes we
+ // could not check if the AccessPoint is ephemeral. Need to cache it in first.
+ mIsEphemeral = mAccessPoint.isEphemeral();
+ mConnectingState = STATE_NONE;
+ mConnectListener = new WifiManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ // Do nothing
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ updateConnectingState(STATE_FAILED);
+ }
+ };
+
+ mIsExpired = mAccessPoint.isExpired();
+ mIsPasspointConfigurationR1 = mAccessPoint.isPasspointConfigurationR1();
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ // Returns null since this controller contains more than one Preference
+ return null;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ setupEntityHeader(screen);
+
+ mButtonsPref = ((ActionButtonsPreference) screen.findPreference(KEY_BUTTONS_PREF))
+ .setButton1Text(R.string.forget)
+ .setButton1Icon(R.drawable.ic_settings_delete)
+ .setButton1OnClickListener(view -> forgetNetwork())
+ .setButton2Text(R.string.wifi_sign_in_button_text)
+ .setButton2Icon(R.drawable.ic_settings_sign_in)
+ .setButton2OnClickListener(view -> signIntoNetwork())
+ .setButton3Text(R.string.wifi_connect)
+ .setButton3Icon(R.drawable.ic_settings_wireless)
+ .setButton3OnClickListener(view -> connectNetwork())
+ .setButton3Enabled(true)
+ .setButton4Text(R.string.share)
+ .setButton4Icon(R.drawable.ic_qrcode_24dp)
+ .setButton4OnClickListener(view -> shareNetwork());
+
+ if (isPasspointConfigurationR1Expired()) {
+ // Hide Connect button.
+ mButtonsPref.setButton3Visible(false);
+ }
+
+ mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF);
+ mTxLinkSpeedPref = screen.findPreference(KEY_TX_LINK_SPEED);
+ mRxLinkSpeedPref = screen.findPreference(KEY_RX_LINK_SPEED);
+ mFrequencyPref = screen.findPreference(KEY_FREQUENCY_PREF);
+ mSecurityPref = screen.findPreference(KEY_SECURITY_PREF);
+
+ mSsidPref = screen.findPreference(KEY_SSID_PREF);
+ mMacAddressPref = screen.findPreference(KEY_MAC_ADDRESS_PREF);
+ mIpAddressPref = screen.findPreference(KEY_IP_ADDRESS_PREF);
+ mGatewayPref = screen.findPreference(KEY_GATEWAY_PREF);
+ mSubnetPref = screen.findPreference(KEY_SUBNET_MASK_PREF);
+ mDnsPref = screen.findPreference(KEY_DNS_PREF);
+
+ mIpv6Category = screen.findPreference(KEY_IPV6_CATEGORY);
+ mIpv6AddressPref = screen.findPreference(KEY_IPV6_ADDRESSES_PREF);
+
+ mSecurityPref.setSummary(mAccessPoint.getSecurityString(/* concise */ false));
+ }
+
+ private void setupEntityHeader(PreferenceScreen screen) {
+ LayoutPreference headerPref = screen.findPreference(KEY_HEADER);
+
+ if (usingDataUsageHeader(mContext)) {
+ headerPref.setVisible(false);
+ mDataUsageSummaryPref = screen.findPreference(KEY_DATA_USAGE_HEADER);
+ mDataUsageSummaryPref.setVisible(true);
+ mSummaryHeaderController =
+ new WifiDataUsageSummaryPreferenceController(mFragment.getActivity(),
+ mLifecycle, (PreferenceFragmentCompat) mFragment, mAccessPoint.getSsid());
+ return;
+ }
+
+ mEntityHeaderController =
+ EntityHeaderController.newInstance(
+ mFragment.getActivity(), mFragment,
+ headerPref.findViewById(R.id.entity_header));
+
+ ImageView iconView = headerPref.findViewById(R.id.entity_header_icon);
+
+ iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+ mEntityHeaderController.setLabel(mAccessPoint.getTitle());
+ }
+
+ private void refreshEntityHeader() {
+ if (usingDataUsageHeader(mContext)) {
+ mSummaryHeaderController.updateState(mDataUsageSummaryPref);
+ } else {
+ String summary;
+ if (isPasspointConfigurationR1Expired()) {
+ // Not able to get summary from AccessPoint because we may lost
+ // PasspointConfiguration information after updateAccessPointFromScannedList().
+ summary = mContext.getResources().getString(
+ com.android.settingslib.R.string.wifi_passpoint_expired);
+ } else {
+ summary = mAccessPoint.getSettingsSummary(true /* convertSavedAsDisconnected */);
+ }
+
+ mEntityHeaderController
+ .setSummary(summary)
+ .setRecyclerView(mFragment.getListView(), mLifecycle)
+ .done(mFragment.getActivity(), true /* rebind */);
+ }
+ }
+
+ private void updateNetworkInfo() {
+ mNetwork = mWifiManager.getCurrentNetwork();
+ mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
+ mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
+ }
+
+ @Override
+ public void onResume() {
+ // Ensure mNetwork is set before any callbacks above are delivered, since our
+ // NetworkCallback only looks at changes to mNetwork.
+ updateNetworkInfo();
+ refreshPage();
+ mContext.registerReceiver(mReceiver, mFilter);
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
+ mHandler);
+ }
+
+ @Override
+ public void onPause() {
+ mNetwork = null;
+ mLinkProperties = null;
+ mNetworkCapabilities = null;
+ mNetworkInfo = null;
+ mWifiInfo = null;
+ mContext.unregisterReceiver(mReceiver);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+
+ private void refreshPage() {
+ if (!updateAccessPoint()) {
+ return;
+ }
+
+ Log.d(TAG, "Update UI!");
+
+ // refresh header
+ refreshEntityHeader();
+
+ // refresh Buttons
+ refreshButtons();
+
+ // Update Connection Header icon and Signal Strength Preference
+ refreshRssiViews();
+ // Frequency Pref
+ refreshFrequency();
+ // Transmit Link Speed Pref
+ refreshTxSpeed();
+ // Receive Link Speed Pref
+ refreshRxSpeed();
+ // IP related information
+ refreshIpLayerInfo();
+ // SSID Pref
+ refreshSsid();
+ // MAC Address Pref
+ refreshMacAddress();
+ }
+
+ @VisibleForTesting
+ boolean updateAccessPoint() {
+ boolean changed = false;
+ // remember mIsOutOfRange as old before updated
+ boolean oldState = mIsOutOfRange;
+ updateAccessPointFromScannedList();
+
+ if (mAccessPoint.isActive()) {
+ updateNetworkInfo();
+ mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
+ mWifiInfo = mWifiManager.getConnectionInfo();
+ if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
+ // Once connected, can't get mNetwork immediately, return false and wait for
+ // next time to update UI. also reset {@code mIsOutOfRange}
+ mIsOutOfRange = oldState;
+ return false;
+ }
+ changed |= mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ }
+
+ // signal level changed
+ changed |= mRssiSignalLevel != mAccessPoint.getLevel();
+ // In/Out of range changed
+ changed |= oldState != mIsOutOfRange;
+ // connect state changed
+ if (mConnected != mAccessPoint.isActive()) {
+ mConnected = mAccessPoint.isActive();
+ changed = true;
+ updateConnectingState(mAccessPoint.isActive() ? STATE_CONNECTED : STATE_DISCONNECTED);
+ }
+
+ return changed;
+ }
+
+ private void updateAccessPointFromScannedList() {
+ mIsOutOfRange = true;
+
+ for (AccessPoint ap : mWifiTracker.getAccessPoints()) {
+ if (mAccessPoint.matches(ap)) {
+ mAccessPoint = ap;
+ mWifiConfig = ap.getConfig();
+ mIsOutOfRange = !mAccessPoint.isReachable();
+ return;
+ }
+ }
+ }
+
+ private void exitActivity() {
+ if (DEBUG) {
+ Log.d(TAG, "Exiting the WifiNetworkDetailsPage");
+ }
+ mFragment.getActivity().finish();
+ }
+
+ private void refreshRssiViews() {
+ int signalLevel = mAccessPoint.getLevel();
+
+ // Disappears signal view if not in range. e.g. for saved networks.
+ if (mIsOutOfRange) {
+ mSignalStrengthPref.setVisible(false);
+ mRssiSignalLevel = -1;
+ return;
+ }
+
+ if (mRssiSignalLevel == signalLevel) {
+ return;
+ }
+ mRssiSignalLevel = signalLevel;
+ Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
+
+ if (mEntityHeaderController != null) {
+ mEntityHeaderController
+ .setIcon(redrawIconForHeader(wifiIcon)).done(mFragment.getActivity(),
+ true /* rebind */);
+ }
+
+ Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
+ wifiIconDark.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal));
+ mSignalStrengthPref.setIcon(wifiIconDark);
+
+ mSignalStrengthPref.setSummary(mSignalStr[mRssiSignalLevel]);
+ mSignalStrengthPref.setVisible(true);
+ }
+
+ private Drawable redrawIconForHeader(Drawable original) {
+ final int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.wifi_detail_page_header_image_size);
+ final int actualWidth = original.getMinimumWidth();
+ final int actualHeight = original.getMinimumHeight();
+
+ if ((actualWidth == iconSize && actualHeight == iconSize)
+ || !VectorDrawable.class.isInstance(original)) {
+ return original;
+ }
+
+ // clear tint list to make sure can set 87% black after enlarge
+ original.setTintList(null);
+
+ // enlarge icon size
+ final Bitmap bitmap = Utils.createBitmap(original,
+ iconSize /*width*/,
+ iconSize /*height*/);
+ Drawable newIcon = new BitmapDrawable(null /*resource*/, bitmap);
+
+ // config color for 87% black after enlarge
+ newIcon.setTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+
+ return newIcon;
+ }
+
+ private void refreshFrequency() {
+ if (mWifiInfo == null) {
+ mFrequencyPref.setVisible(false);
+ return;
+ }
+
+ final int frequency = mWifiInfo.getFrequency();
+ String band = null;
+ if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
+ && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
+ band = mContext.getResources().getString(R.string.wifi_band_24ghz);
+ } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
+ && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
+ band = mContext.getResources().getString(R.string.wifi_band_5ghz);
+ } else {
+ Log.e(TAG, "Unexpected frequency " + frequency);
+ // Connecting state is unstable, make it disappeared if unexpected
+ if (mConnectingState == STATE_CONNECTING) {
+ mFrequencyPref.setVisible(false);
+ }
+ return;
+ }
+ mFrequencyPref.setSummary(band);
+ mFrequencyPref.setVisible(true);
+ }
+
+ private void refreshTxSpeed() {
+ if (mWifiInfo == null) {
+ mTxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps();
+ mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0);
+ mTxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps()));
+ }
+
+ private void refreshRxSpeed() {
+ if (mWifiInfo == null) {
+ mRxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ int rxLinkSpeedMbps = mWifiInfo.getRxLinkSpeedMbps();
+ mRxLinkSpeedPref.setVisible(rxLinkSpeedMbps >= 0);
+ mRxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.rx_link_speed, mWifiInfo.getRxLinkSpeedMbps()));
+ }
+
+ private void refreshSsid() {
+ if (mAccessPoint.isPasspoint() || mAccessPoint.isOsuProvider()) {
+ mSsidPref.setVisible(true);
+ mSsidPref.setSummary(mAccessPoint.getSsidStr());
+ } else {
+ mSsidPref.setVisible(false);
+ }
+ }
+
+ private void refreshMacAddress() {
+ String macAddress = getMacAddress();
+ if (macAddress == null) {
+ mMacAddressPref.setVisible(false);
+ return;
+ }
+
+ mMacAddressPref.setVisible(true);
+ if (macAddress.equals(WifiInfo.DEFAULT_MAC_ADDRESS)) {
+ mMacAddressPref.setSummary(R.string.device_info_not_available);
+ } else {
+ mMacAddressPref.setSummary(macAddress);
+ }
+
+ // MAC Address Pref Title
+ refreshMacTitle();
+ }
+
+ private String getMacAddress() {
+ if (mWifiInfo != null) {
+ // get MAC address from connected network information
+ return mWifiInfo.getMacAddress();
+ }
+
+ // return randomized MAC address
+ if (mWifiConfig != null && mWifiConfig.macRandomizationSetting
+ == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
+ return mWifiConfig.getRandomizedMacAddress().toString();
+ }
+
+ // return device MAC address
+ final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+ if (macAddresses != null && macAddresses.length > 0) {
+ return macAddresses[0];
+ }
+
+ Log.e(TAG, "Can't get device MAC address!");
+ return null;
+ }
+
+ private void updatePreference(Preference pref, String detailText) {
+ if (!TextUtils.isEmpty(detailText)) {
+ pref.setSummary(detailText);
+ pref.setVisible(true);
+ } else {
+ pref.setVisible(false);
+ }
+ }
+
+ private void refreshButtons() {
+ // Ephemeral network won't be removed permanently, but be putted in blacklist.
+ mButtonsPref.setButton1Text(
+ mIsEphemeral ? R.string.wifi_disconnect_button_text : R.string.forget);
+
+ boolean canForgetNetwork = canForgetNetwork();
+ boolean canSignIntoNetwork = canSignIntoNetwork();
+ boolean canConnectNetwork = canConnectNetwork() && !isPasspointConfigurationR1Expired();
+ boolean canShareNetwork = canShareNetwork();
+
+ mButtonsPref.setButton1Visible(canForgetNetwork);
+ mButtonsPref.setButton2Visible(canSignIntoNetwork);
+ mButtonsPref.setButton3Visible(canConnectNetwork);
+ mButtonsPref.setButton4Visible(canShareNetwork);
+ mButtonsPref.setVisible(canForgetNetwork
+ || canSignIntoNetwork
+ || canConnectNetwork
+ || canShareNetwork);
+ }
+
+ private boolean canConnectNetwork() {
+ // Display connect button for disconnected AP even not in the range.
+ return !mAccessPoint.isActive();
+ }
+
+ private boolean isPasspointConfigurationR1Expired() {
+ return mIsPasspointConfigurationR1 && mIsExpired;
+ }
+
+ private void refreshIpLayerInfo() {
+ // Hide IP layer info if not a connected network.
+ if (!mAccessPoint.isActive() || mNetwork == null || mLinkProperties == null) {
+ mIpAddressPref.setVisible(false);
+ mSubnetPref.setVisible(false);
+ mGatewayPref.setVisible(false);
+ mDnsPref.setVisible(false);
+ mIpv6Category.setVisible(false);
+ return;
+ }
+
+ // Find IPv4 and IPv6 addresses.
+ String ipv4Address = null;
+ String subnet = null;
+ StringJoiner ipv6Addresses = new StringJoiner("\n");
+
+ for (LinkAddress addr : mLinkProperties.getLinkAddresses()) {
+ if (addr.getAddress() instanceof Inet4Address) {
+ ipv4Address = addr.getAddress().getHostAddress();
+ subnet = ipv4PrefixLengthToSubnetMask(addr.getPrefixLength());
+ } else if (addr.getAddress() instanceof Inet6Address) {
+ ipv6Addresses.add(addr.getAddress().getHostAddress());
+ }
+ }
+
+ // Find IPv4 default gateway.
+ String gateway = null;
+ for (RouteInfo routeInfo : mLinkProperties.getRoutes()) {
+ if (routeInfo.isIPv4Default() && routeInfo.hasGateway()) {
+ gateway = routeInfo.getGateway().getHostAddress();
+ break;
+ }
+ }
+
+ // Find all (IPv4 and IPv6) DNS addresses.
+ String dnsServers = mLinkProperties.getDnsServers().stream()
+ .map(InetAddress::getHostAddress)
+ .collect(Collectors.joining("\n"));
+
+ // Update UI.
+ updatePreference(mIpAddressPref, ipv4Address);
+ updatePreference(mSubnetPref, subnet);
+ updatePreference(mGatewayPref, gateway);
+ updatePreference(mDnsPref, dnsServers);
+
+ if (ipv6Addresses.length() > 0) {
+ mIpv6AddressPref.setSummary(
+ BidiFormatter.getInstance().unicodeWrap(ipv6Addresses.toString()));
+ mIpv6Category.setVisible(true);
+ } else {
+ mIpv6Category.setVisible(false);
+ }
+ }
+
+ private static String ipv4PrefixLengthToSubnetMask(int prefixLength) {
+ try {
+ InetAddress all = InetAddress.getByAddress(
+ new byte[]{(byte) 255, (byte) 255, (byte) 255, (byte) 255});
+ return NetworkUtils.getNetworkPart(all, prefixLength).getHostAddress();
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns whether the network represented by this preference can be forgotten.
+ */
+ private boolean canForgetNetwork() {
+ return (mWifiInfo != null && mWifiInfo.isEphemeral()) || canModifyNetwork()
+ || mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig();
+ }
+
+ /**
+ * Returns whether the network represented by this preference can be modified.
+ */
+ public boolean canModifyNetwork() {
+ return mWifiConfig != null && !WifiUtils.isNetworkLockedDown(mContext, mWifiConfig);
+ }
+
+ /**
+ * Returns whether the user can sign into the network represented by this preference.
+ */
+ private boolean canSignIntoNetwork() {
+ return mAccessPoint.isActive() && WifiUtils.canSignIntoNetwork(mNetworkCapabilities);
+ }
+
+ /**
+ * Returns whether the user can share the network represented by this preference with QR code.
+ */
+ private boolean canShareNetwork() {
+ return mAccessPoint.getConfig() != null
+ && WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mContext, mAccessPoint);
+ }
+
+ /**
+ * Forgets the wifi network associated with this preference.
+ */
+ private void forgetNetwork() {
+ if (mWifiInfo != null && mWifiInfo.isEphemeral()) {
+ mWifiManager.disableEphemeralNetwork(mWifiInfo.getSSID());
+ } else if (mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig()) {
+ // Post a dialog to confirm if user really want to forget the passpoint network.
+ showConfirmForgetDialog();
+ return;
+ } else if (mWifiConfig != null) {
+ mWifiManager.forget(mWifiConfig.networkId, null /* action listener */);
+ }
+
+ mMetricsFeatureProvider.action(
+ mFragment.getActivity(), SettingsEnums.ACTION_WIFI_FORGET);
+ mFragment.getActivity().finish();
+ }
+
+ @VisibleForTesting
+ protected void showConfirmForgetDialog() {
+ final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setPositiveButton(R.string.forget, ((dialog1, which) -> {
+ try {
+ mWifiManager.removePasspointConfiguration(mAccessPoint.getPasspointFqdn());
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to remove Passpoint configuration for "
+ + mAccessPoint.getPasspointFqdn());
+ }
+ mMetricsFeatureProvider.action(
+ mFragment.getActivity(), SettingsEnums.ACTION_WIFI_FORGET);
+ mFragment.getActivity().finish();
+ }))
+ .setNegativeButton(R.string.cancel, null /* listener */)
+ .setTitle(R.string.wifi_forget_dialog_title)
+ .setMessage(R.string.forget_passpoint_dialog_message)
+ .create();
+ dialog.show();
+ }
+
+ /**
+ * Show QR code to share the network represented by this preference.
+ */
+ private void launchWifiDppConfiguratorActivity() {
+ final Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntentOrNull(mContext,
+ mWifiManager, mAccessPoint);
+
+ if (intent == null) {
+ Log.e(TAG, "Launch Wi-Fi DPP QR code generator with a wrong Wi-Fi network!");
+ } else {
+ mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
+ SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_QR_CODE,
+ SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR,
+ /* key */ null,
+ /* value */ Integer.MIN_VALUE);
+
+ mContext.startActivity(intent);
+ }
+ }
+
+ /**
+ * Share the wifi network with QR code.
+ */
+ private void shareNetwork() {
+ WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorActivity());
+ }
+
+ /**
+ * Sign in to the captive portal found on this wifi network associated with this preference.
+ */
+ private void signIntoNetwork() {
+ mMetricsFeatureProvider.action(
+ mFragment.getActivity(), SettingsEnums.ACTION_WIFI_SIGNIN);
+ mConnectivityManager.startCaptivePortalApp(mNetwork);
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ if (dialog.getController() != null) {
+ mWifiManager.save(dialog.getController().getConfig(), new WifiManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ Activity activity = mFragment.getActivity();
+ if (activity != null) {
+ Toast.makeText(activity,
+ R.string.wifi_failed_save_message,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Wrapper for testing compatibility.
+ */
+ @VisibleForTesting
+ static class IconInjector {
+ private final Context mContext;
+
+ IconInjector(Context context) {
+ mContext = context;
+ }
+
+ public Drawable getIcon(int level) {
+ return mContext.getDrawable(Utils.getWifiIconResource(level)).mutate();
+ }
+ }
+
+ private boolean usingDataUsageHeader(Context context) {
+ return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER);
+ }
+
+ @VisibleForTesting
+ void connectNetwork() {
+ final Activity activity = mFragment.getActivity();
+ // error handling, connected/saved network should have mWifiConfig.
+ if (mWifiConfig == null) {
+ Toast.makeText(activity,
+ R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // init state before connect
+ mConnectingState = STATE_NONE;
+
+ if (mWifiManager.isWifiEnabled()) {
+ updateConnectingState(STATE_CONNECTING);
+ } else {
+ // Enable Wi-Fi automatically to connect AP
+ updateConnectingState(STATE_ENABLE_WIFI);
+ }
+ }
+
+ private void updateConnectingState(int state) {
+ final Activity activity = mFragment.getActivity();
+ Log.d(TAG, "updateConnectingState from " + mConnectingState + " to " + state);
+ switch (mConnectingState) {
+ case STATE_NONE:
+ case STATE_ENABLE_WIFI:
+ if (state == STATE_ENABLE_WIFI) {
+ Log.d(TAG, "Turn on Wi-Fi automatically!");
+ updateConnectedButton(STATE_ENABLE_WIFI);
+ Toast.makeText(activity,
+ R.string.wifi_turned_on_message,
+ Toast.LENGTH_SHORT).show();
+ mWifiManager.setWifiEnabled(true);
+ // start timer for error handling
+ startTimer();
+ } else if (state == STATE_CONNECTING) {
+ Log.d(TAG, "connecting...");
+ updateConnectedButton(STATE_CONNECTING);
+ if (mAccessPoint.isPasspoint()) {
+ mWifiManager.connect(mWifiConfig, mConnectListener);
+ } else {
+ mWifiManager.connect(mWifiConfig.networkId, mConnectListener);
+ }
+ // start timer for error handling since framework didn't call back if failed
+ startTimer();
+ } else if (state == STATE_ENABLE_WIFI_FAILED) {
+ Log.e(TAG, "Wi-Fi failed to enable network!");
+ stopTimer();
+ // reset state
+ state = STATE_NONE;
+ Toast.makeText(activity,
+ R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ updateConnectedButton(STATE_ENABLE_WIFI_FAILED);
+ }
+ // Do not break here for disconnected event.
+ case STATE_CONNECTED:
+ if (state == STATE_DISCONNECTED) {
+ Log.d(TAG, "disconnected");
+ // reset state
+ state = STATE_NONE;
+ updateConnectedButton(STATE_DISCONNECTED);
+ refreshPage();
+ // clear for getting MAC Address from saved configuration
+ mWifiInfo = null;
+ }
+ break;
+ case STATE_CONNECTING:
+ if (state == STATE_CONNECTED) {
+ Log.d(TAG, "connected");
+ stopTimer();
+ updateConnectedButton(STATE_CONNECTED);
+ Toast.makeText(activity,
+ mContext.getString(R.string.wifi_connected_to_message,
+ mAccessPoint.getTitle()),
+ Toast.LENGTH_SHORT).show();
+
+ refreshPage();
+ } else if (state == STATE_NOT_IN_RANGE) {
+ Log.d(TAG, "AP not in range");
+ stopTimer();
+ // reset state
+ state = STATE_NONE;
+ Toast.makeText(activity,
+ R.string.wifi_not_in_range_message,
+ Toast.LENGTH_SHORT).show();
+ updateConnectedButton(STATE_NOT_IN_RANGE);
+ } else if (state == STATE_FAILED) {
+ Log.d(TAG, "failed");
+ stopTimer();
+ // reset state
+ state = STATE_NONE;
+ Toast.makeText(activity,
+ R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ updateConnectedButton(STATE_FAILED);
+ }
+ break;
+ default:
+ Log.e(TAG, "Invalid state : " + mConnectingState);
+ // don't update invalid state
+ return;
+ }
+
+ mConnectingState = state;
+ }
+
+ private void updateConnectedButton(int state) {
+ switch (state) {
+ case STATE_ENABLE_WIFI:
+ case STATE_CONNECTING:
+ mButtonsPref.setButton3Text(R.string.wifi_connecting)
+ .setButton3Enabled(false);
+ break;
+ case STATE_CONNECTED:
+ // init button state and set as invisible
+ mButtonsPref.setButton3Text(R.string.wifi_connect)
+ .setButton3Icon(R.drawable.ic_settings_wireless)
+ .setButton3Enabled(true)
+ .setButton3Visible(false);
+ break;
+ case STATE_DISCONNECTED:
+ case STATE_NOT_IN_RANGE:
+ case STATE_FAILED:
+ case STATE_ENABLE_WIFI_FAILED:
+ if (isPasspointConfigurationR1Expired()) {
+ // Hide Connect button.
+ mButtonsPref.setButton3Visible(false);
+ } else {
+ mButtonsPref.setButton3Text(R.string.wifi_connect)
+ .setButton3Icon(R.drawable.ic_settings_wireless)
+ .setButton3Enabled(true)
+ .setButton3Visible(true);
+ }
+ break;
+ default:
+ Log.e(TAG, "Invalid connect button state : " + state);
+ break;
+ }
+ }
+
+ private void startTimer() {
+ if (sTimer != null) {
+ stopTimer();
+ }
+
+ sTimer = new CountDownTimer(TIMEOUT, TIMEOUT + 1) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ // Do nothing
+ }
+ @Override
+ public void onFinish() {
+ if (mFragment == null || mFragment.getActivity() == null) {
+ Log.d(TAG, "Ignore timeout since activity not exist!");
+ return;
+ }
+ Log.e(TAG, "Timeout for state:" + mConnectingState);
+ if (mConnectingState == STATE_ENABLE_WIFI) {
+ updateConnectingState(STATE_ENABLE_WIFI_FAILED);
+ } else if (mConnectingState == STATE_CONNECTING) {
+ updateAccessPointFromScannedList();
+ if (mIsOutOfRange) {
+ updateConnectingState(STATE_NOT_IN_RANGE);
+ } else {
+ updateConnectingState(STATE_FAILED);
+ }
+ }
+ }
+ };
+ sTimer.start();
+ }
+
+ private void stopTimer() {
+ if (sTimer == null) return;
+
+ sTimer.cancel();
+ sTimer = null;
+ }
+
+ private void refreshMacTitle() {
+ if (mWifiConfig == null) {
+ return;
+ }
+
+ // For saved Passpoint network, framework doesn't have the field to keep the MAC choice
+ // persistently, so Passpoint network will always use the default value so far, which is
+ // randomized MAC address, so don't need to modify title.
+ if (mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig()) {
+ return;
+ }
+
+ mMacAddressPref.setTitle(
+ (mWifiConfig.macRandomizationSetting
+ == WifiConfiguration.RANDOMIZATION_PERSISTENT)
+ ? R.string.wifi_advanced_randomized_mac_address_title
+ : R.string.wifi_advanced_device_mac_address_title);
+
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java
new file mode 100644
index 0000000..99967dc
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import android.app.backup.BackupManager;
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.DropDownPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * {@link AbstractPreferenceController} that controls whether the wifi network is metered or not
+ */
+public class WifiMeteredPreferenceController2 extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener, WifiDialog.WifiDialogListener {
+
+ private static final String KEY_WIFI_METERED = "metered";
+ private WifiConfiguration mWifiConfiguration;
+ private WifiManager mWifiManager;
+ private Preference mPreference;
+
+ public WifiMeteredPreferenceController2(Context context, WifiConfiguration wifiConfiguration) {
+ super(context, KEY_WIFI_METERED);
+ mWifiConfiguration = wifiConfiguration;
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final DropDownPreference dropDownPreference = (DropDownPreference) preference;
+ final int meteredOverride = getMeteredOverride();
+ dropDownPreference.setValue(Integer.toString(meteredOverride));
+ updateSummary(dropDownPreference, meteredOverride);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mWifiConfiguration != null) {
+ mWifiConfiguration.meteredOverride = Integer.parseInt((String) newValue);
+ }
+ mWifiManager.updateNetwork(mWifiConfiguration);
+ // Stage the backup of the SettingsProvider package which backs this up
+ BackupManager.dataChanged("com.android.providers.settings");
+ updateSummary((DropDownPreference) preference, getMeteredOverride());
+ return true;
+ }
+
+ @VisibleForTesting
+ int getMeteredOverride() {
+ if (mWifiConfiguration != null) {
+ // Wrap the meteredOverride since robolectric cannot recognize it
+ return mWifiConfiguration.meteredOverride;
+ }
+ return WifiConfiguration.METERED_OVERRIDE_NONE;
+ }
+
+ private void updateSummary(DropDownPreference preference, int meteredOverride) {
+ preference.setSummary(preference.getEntries()[meteredOverride]);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ if (dialog.getController() != null) {
+ final WifiConfiguration newConfig = dialog.getController().getConfig();
+ if (newConfig == null || mWifiConfiguration == null) {
+ return;
+ }
+
+ if (newConfig.meteredOverride != mWifiConfiguration.meteredOverride) {
+ mWifiConfiguration = newConfig;
+ onPreferenceChange(mPreference, String.valueOf(newConfig.meteredOverride));
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
new file mode 100644
index 0000000..5eb4b28
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static com.android.settings.wifi.WifiSettings.WIFI_DIALOG_ID;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.wifi.WifiConfigUiBase;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.wifi.AccessPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Detail page for the currently connected wifi network.
+ *
+ * <p>The AccessPoint should be saved to the intent Extras when launching this class via
+ * {@link AccessPoint#saveWifiState(Bundle)} in order to properly render this page.
+ */
+public class WifiNetworkDetailsFragment2 extends DashboardFragment implements
+ WifiDialog.WifiDialogListener {
+
+ private static final String TAG = "WifiNetworkDetailsFrg2";
+
+ private AccessPoint mAccessPoint;
+ private WifiDetailPreferenceController2 mWifiDetailPreferenceController2;
+ private List<WifiDialog.WifiDialogListener> mWifiDialogListeners = new ArrayList<>();
+
+ @Override
+ public void onAttach(Context context) {
+ mAccessPoint = new AccessPoint(context, getArguments());
+ super.onAttach(context);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.WIFI_NETWORK_DETAILS;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.wifi_network_details_fragment;
+ }
+
+ @Override
+ public int getDialogMetricsCategory(int dialogId) {
+ if (dialogId == WIFI_DIALOG_ID) {
+ return SettingsEnums.DIALOG_WIFI_AP_EDIT;
+ }
+ return 0;
+ }
+
+ @Override
+ public Dialog onCreateDialog(int dialogId) {
+ if (getActivity() == null || mWifiDetailPreferenceController2 == null
+ || mAccessPoint == null) {
+ return null;
+ }
+ return WifiDialog.createModal(getActivity(), this, mAccessPoint,
+ WifiConfigUiBase.MODE_MODIFY);
+ }
+
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
+ item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case Menu.FIRST:
+ if (!mWifiDetailPreferenceController2.canModifyNetwork()) {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
+ RestrictedLockUtilsInternal.getDeviceOwner(getContext()));
+ } else {
+ showDialog(WIFI_DIALOG_ID);
+ }
+ return true;
+ default:
+ return super.onOptionsItemSelected(menuItem);
+ }
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+
+ mWifiDetailPreferenceController2 = WifiDetailPreferenceController2.newInstance(
+ mAccessPoint,
+ cm,
+ context,
+ this,
+ new Handler(Looper.getMainLooper()), // UI thread.
+ getSettingsLifecycle(),
+ context.getSystemService(WifiManager.class),
+ mMetricsFeatureProvider);
+
+ controllers.add(mWifiDetailPreferenceController2);
+ controllers.add(new AddDevicePreferenceController2(context).init(mAccessPoint));
+
+ final WifiMeteredPreferenceController2 meteredPreferenceController2 =
+ new WifiMeteredPreferenceController2(context, mAccessPoint.getConfig());
+ controllers.add(meteredPreferenceController2);
+
+ final WifiPrivacyPreferenceController2 privacyController2 =
+ new WifiPrivacyPreferenceController2(context);
+ privacyController2.setWifiConfiguration(mAccessPoint.getConfig());
+ privacyController2.setIsEphemeral(mAccessPoint.isEphemeral());
+ privacyController2.setIsPasspoint(
+ mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig());
+ controllers.add(privacyController2);
+
+ // Sets callback listener for wifi dialog.
+ mWifiDialogListeners.add(mWifiDetailPreferenceController2);
+ mWifiDialogListeners.add(privacyController2);
+ mWifiDialogListeners.add(meteredPreferenceController2);
+
+ return controllers;
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ for (WifiDialog.WifiDialogListener listener : mWifiDialogListeners) {
+ listener.onSubmit(dialog);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
new file mode 100644
index 0000000..d85b607
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.DropDownPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * {@link AbstractPreferenceController} that controls whether the wifi network is mac randomized
+ * or not
+ */
+public class WifiPrivacyPreferenceController2 extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener, WifiDialog.WifiDialogListener {
+
+ private static final String KEY_WIFI_PRIVACY = "privacy";
+ private WifiConfiguration mWifiConfiguration;
+ private WifiManager mWifiManager;
+ private boolean mIsEphemeral = false;
+ private boolean mIsPasspoint = false;
+ private Preference mPreference;
+
+ public WifiPrivacyPreferenceController2(Context context) {
+ super(context, KEY_WIFI_PRIVACY);
+ mWifiConfiguration = null;
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ public void setWifiConfiguration(WifiConfiguration wifiConfiguration) {
+ mWifiConfiguration = wifiConfiguration;
+ }
+
+ public void setIsEphemeral(boolean isEphemeral) {
+ mIsEphemeral = isEphemeral;
+ }
+
+ public void setIsPasspoint(boolean isPasspoint) {
+ mIsPasspoint = isPasspoint;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mWifiManager.isConnectedMacRandomizationSupported()
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final DropDownPreference dropDownPreference = (DropDownPreference) preference;
+ final int randomizationLevel = getRandomizationValue();
+ dropDownPreference.setValue(Integer.toString(randomizationLevel));
+ updateSummary(dropDownPreference, randomizationLevel);
+
+ // Makes preference not selectable, when this is a ephemeral network.
+ if (mIsEphemeral || mIsPasspoint) {
+ preference.setSelectable(false);
+ dropDownPreference.setSummary(R.string.wifi_privacy_settings_ephemeral_summary);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mWifiConfiguration != null) {
+ mWifiConfiguration.macRandomizationSetting = Integer.parseInt((String) newValue);
+ mWifiManager.updateNetwork(mWifiConfiguration);
+
+ // To activate changing, we need to reconnect network. WiFi will auto connect to
+ // current network after disconnect(). Only needed when this is connected network.
+ final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (wifiInfo != null && wifiInfo.getNetworkId() == mWifiConfiguration.networkId) {
+ mWifiManager.disconnect();
+ }
+ }
+ updateSummary((DropDownPreference) preference, Integer.parseInt((String) newValue));
+ return true;
+ }
+
+ @VisibleForTesting
+ int getRandomizationValue() {
+ if (mWifiConfiguration != null) {
+ return mWifiConfiguration.macRandomizationSetting;
+ }
+ return WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ }
+
+ private static final int PREF_RANDOMIZATION_PERSISTENT = 0;
+ private static final int PREF_RANDOMIZATION_NONE = 1;
+
+ /**
+ * Returns preference index value.
+ *
+ * @param macRandomized is mac randomized value
+ * @return index value of preference
+ */
+ public static int translateMacRandomizedValueToPrefValue(int macRandomized) {
+ return (macRandomized == WifiConfiguration.RANDOMIZATION_PERSISTENT)
+ ? PREF_RANDOMIZATION_PERSISTENT : PREF_RANDOMIZATION_NONE;
+ }
+
+ /**
+ * Returns mac randomized value.
+ *
+ * @param prefMacRandomized is preference index value
+ * @return mac randomized value
+ */
+ public static int translatePrefValueToMacRandomizedValue(int prefMacRandomized) {
+ return (prefMacRandomized == PREF_RANDOMIZATION_PERSISTENT)
+ ? WifiConfiguration.RANDOMIZATION_PERSISTENT : WifiConfiguration.RANDOMIZATION_NONE;
+ }
+
+ private void updateSummary(DropDownPreference preference, int macRandomized) {
+ // Translates value here to set RANDOMIZATION_PERSISTENT as first item in UI for better UX.
+ final int prefMacRandomized = translateMacRandomizedValueToPrefValue(macRandomized);
+ preference.setSummary(preference.getEntries()[prefMacRandomized]);
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ if (dialog.getController() != null) {
+ final WifiConfiguration newConfig = dialog.getController().getConfig();
+ if (newConfig == null || mWifiConfiguration == null) {
+ return;
+ }
+
+ if (newConfig.macRandomizationSetting != mWifiConfiguration.macRandomizationSetting) {
+ mWifiConfiguration = newConfig;
+ onPreferenceChange(mPreference, String.valueOf(newConfig.macRandomizationSetting));
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2.java b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2.java
new file mode 100644
index 0000000..3b8eb27
--- /dev/null
+++ b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.AccessPointPreference.UserBadgeCache;
+import com.android.settingslib.wifi.WifiSavedConfigUtils;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Controller that manages a PreferenceGroup, which contains a list of saved access points.
+ */
+public class SavedAccessPointsPreferenceController2 extends BasePreferenceController implements
+ Preference.OnPreferenceClickListener {
+
+ protected final WifiManager mWifiManager;
+ private final UserBadgeCache mUserBadgeCache;
+ private PreferenceGroup mPreferenceGroup;
+ private SavedAccessPointsWifiSettings2 mHost;
+ @VisibleForTesting
+ List<AccessPoint> mAccessPoints;
+
+ public SavedAccessPointsPreferenceController2(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mUserBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager());
+ mWifiManager = context.getSystemService(WifiManager.class);
+ }
+
+ /**
+ * Set {@link SavedAccessPointsWifiSettings2} for click callback action.
+ */
+ public SavedAccessPointsPreferenceController2 setHost(SavedAccessPointsWifiSettings2 host) {
+ mHost = host;
+ return this;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mAccessPoints.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreferenceGroup = screen.findPreference(getPreferenceKey());
+ refreshSavedAccessPoints();
+ updatePreference();
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (mHost != null) {
+ final Preference preferenceInGroup =
+ mPreferenceGroup.findPreference(preference.getKey());
+ mHost.showWifiPage((AccessPointPreference) preferenceInGroup);
+ }
+ return false;
+ }
+
+ protected void refreshSavedAccessPoints() {
+ mAccessPoints = WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager).stream()
+ .filter(accessPoint -> !accessPoint.isPasspointConfig())
+ .sorted(SavedNetworkComparator2.INSTANCE)
+ .collect(Collectors.toList());
+ }
+
+ private void updatePreference() {
+ mPreferenceGroup.removeAll();
+ for (AccessPoint accessPoint : mAccessPoints) {
+ final String key = accessPoint.getKey();
+
+ final AccessPointPreference preference = new AccessPointPreference(accessPoint,
+ mContext, mUserBadgeCache, true /* forSavedNetworks */);
+ preference.setKey(key);
+ preference.setIcon(null);
+ preference.setOnPreferenceClickListener(this);
+
+ mPreferenceGroup.addPreference(preference);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2.java b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2.java
new file mode 100644
index 0000000..a1b7733
--- /dev/null
+++ b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import android.annotation.Nullable;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.wifi.WifiSettings;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+
+/**
+ * UI to manage saved networks/access points.
+ */
+public class SavedAccessPointsWifiSettings2 extends DashboardFragment {
+
+ private static final String TAG = "SavedAccessPoints2";
+
+ @VisibleForTesting
+ Bundle mAccessPointSavedState;
+ private AccessPoint mSelectedAccessPoint;
+
+ // Instance state key
+ private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.WIFI_SAVED_ACCESS_POINTS;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.wifi_display_saved_access_points2;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ use(SavedAccessPointsPreferenceController2.class)
+ .setHost(this);
+ use(SubscribedAccessPointsPreferenceController2.class)
+ .setHost(this);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
+ mAccessPointSavedState =
+ savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
+ } else {
+ mAccessPointSavedState = null;
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (mAccessPointSavedState != null) {
+ final PreferenceScreen screen = getPreferenceScreen();
+ use(SavedAccessPointsPreferenceController2.class).displayPreference(screen);
+ use(SubscribedAccessPointsPreferenceController2.class).displayPreference(screen);
+ }
+ }
+
+ /**
+ * Shows {@link WifiNetworkDetailsFragment2} for assigned {@link AccessPointPreference}.
+ */
+ public void showWifiPage(@Nullable AccessPointPreference accessPoint) {
+ removeDialog(WifiSettings.WIFI_DIALOG_ID);
+
+ if (accessPoint != null) {
+ // Save the access point and edit mode
+ mSelectedAccessPoint = accessPoint.getAccessPoint();
+ } else {
+ // No access point is selected. Clear saved state.
+ mSelectedAccessPoint = null;
+ mAccessPointSavedState = null;
+ }
+
+ if (mSelectedAccessPoint == null) {
+ mSelectedAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState);
+ }
+ final Bundle savedState = new Bundle();
+ mSelectedAccessPoint.saveWifiState(savedState);
+
+ new SubSettingLauncher(getContext())
+ .setTitleText(mSelectedAccessPoint.getTitle())
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(savedState)
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ // If the dialog is showing (indicated by the existence of mSelectedAccessPoint), then we
+ // save its state.
+ if (mSelectedAccessPoint != null) {
+ mAccessPointSavedState = new Bundle();
+ mSelectedAccessPoint.saveWifiState(mAccessPointSavedState);
+ outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/savedaccesspoints2/SavedNetworkComparator2.java b/src/com/android/settings/wifi/savedaccesspoints2/SavedNetworkComparator2.java
new file mode 100644
index 0000000..8388531
--- /dev/null
+++ b/src/com/android/settings/wifi/savedaccesspoints2/SavedNetworkComparator2.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import android.icu.text.Collator;
+
+import com.android.settingslib.wifi.AccessPoint;
+
+import java.util.Comparator;
+
+/**
+ * For {@link AccessPoint} sorting before desplaying.
+ */
+public final class SavedNetworkComparator2 {
+ public static final Comparator<AccessPoint> INSTANCE =
+ new Comparator<AccessPoint>() {
+ final Collator mCollator = Collator.getInstance();
+
+ @Override
+ public int compare(AccessPoint ap1, AccessPoint ap2) {
+ return mCollator.compare(
+ nullToEmpty(ap1.getTitle()), nullToEmpty(ap2.getTitle()));
+ }
+
+ private String nullToEmpty(String string) {
+ return (string == null) ? "" : string;
+ }
+ };
+}
diff --git a/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2.java b/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2.java
new file mode 100644
index 0000000..00ae221
--- /dev/null
+++ b/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import android.content.Context;
+
+import com.android.settingslib.wifi.WifiSavedConfigUtils;
+
+import java.util.stream.Collectors;
+
+/**
+ * Controller that manages a PreferenceGroup, which contains a list of subscribed access points.
+ */
+public class SubscribedAccessPointsPreferenceController2 extends
+ SavedAccessPointsPreferenceController2 {
+
+ public SubscribedAccessPointsPreferenceController2(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ protected void refreshSavedAccessPoints() {
+ mAccessPoints = WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager).stream()
+ .filter(accessPoint -> accessPoint.isPasspointConfig())
+ .sorted(SavedNetworkComparator2.INSTANCE)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index e5c8de5..97ac9c5 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -38,6 +38,7 @@
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
@@ -54,8 +55,10 @@
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.WifiDialogActivity;
import com.android.settings.wifi.WifiSettings;
+import com.android.settings.wifi.WifiSettings2;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.settingslib.wifi.AccessPoint;
import java.util.Arrays;
@@ -247,13 +250,24 @@
accessPoint.saveWifiState(extras);
if (accessPoint.isActive()) {
- final Intent intent = new SubSettingLauncher(mContext)
- .setTitleRes(R.string.pref_title_network_details)
- .setDestination(WifiNetworkDetailsFragment.class.getName())
- .setArguments(extras)
- .setSourceMetricsCategory(SettingsEnums.WIFI)
- .toIntent();
- return getActivityAction(requestCode, intent, icon, title);
+ Intent intent;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ intent = new SubSettingLauncher(mContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(extras)
+ .setSourceMetricsCategory(SettingsEnums.WIFI)
+ .toIntent();
+ return getActivityAction(requestCode, intent, icon, title);
+ } else {
+ intent = new SubSettingLauncher(mContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment.class.getName())
+ .setArguments(extras)
+ .setSourceMetricsCategory(SettingsEnums.WIFI)
+ .toIntent();
+ return getActivityAction(requestCode, intent, icon, title);
+ }
} else if (WifiUtils.getConnectingType(accessPoint) != WifiUtils.CONNECT_TYPE_OTHERS) {
final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
.putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
@@ -317,11 +331,21 @@
public Intent getIntent() {
final String screenTitle = mContext.getText(R.string.wifi_settings).toString();
final Uri contentUri = new Uri.Builder().appendPath(KEY_WIFI).build();
- final Intent intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+
+ Intent intent;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+ WifiSettings2.class.getName(), KEY_WIFI, screenTitle,
+ SettingsEnums.DIALOG_WIFI_AP_EDIT)
+ .setClassName(mContext.getPackageName(), SubSettings.class.getName())
+ .setData(contentUri);
+ } else {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
WifiSettings.class.getName(), KEY_WIFI, screenTitle,
SettingsEnums.DIALOG_WIFI_AP_EDIT)
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
.setData(contentUri);
+ }
return intent;
}
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
new file mode 100644
index 0000000..8d15224
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
@@ -0,0 +1,1905 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.widget.ActionButtonsPreference;
+import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowToast;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowDevicePolicyManager.class, ShadowEntityHeaderController.class})
+public class WifiDetailPreferenceController2Test {
+
+ private static final int LEVEL = 1;
+ private static final int RSSI = -55;
+ private static final int TX_LINK_SPEED = 123;
+ private static final int RX_LINK_SPEED = 54;
+ private static final String SSID = "ssid";
+ private static final String MAC_ADDRESS = "01:23:45:67:89:ab";
+ private static final String RANDOMIZED_MAC_ADDRESS = "RANDOMIZED_MAC_ADDRESS";
+ private static final String FACTORY_MAC_ADDRESS = "FACTORY_MAC_ADDRESS";
+ private static final String SECURITY = "None";
+ private static final String FQDN = "fqdn";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mMockScreen;
+
+ @Mock
+ private AccessPoint mMockAccessPoint;
+ @Mock
+ private FragmentActivity mMockActivity;
+ @Mock
+ private ConnectivityManager mMockConnectivityManager;
+ @Mock
+ private Network mMockNetwork;
+ @Mock
+ private NetworkInfo mMockNetworkInfo;
+ @Mock
+ private WifiConfiguration mMockWifiConfig;
+ @Mock
+ private WifiInfo mMockWifiInfo;
+ @Mock
+ private WifiNetworkDetailsFragment2 mMockFragment;
+ @Mock
+ private WifiManager mMockWifiManager;
+ @Mock
+ private WifiTracker mMockWifiTracker;
+ @Mock
+ private MetricsFeatureProvider mMockMetricsFeatureProvider;
+ @Mock
+ private WifiDetailPreferenceController2.IconInjector mMockIconInjector;
+ @Mock
+ private MacAddress mMockMacAddress;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private EntityHeaderController mMockHeaderController;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private LayoutPreference mMockHeaderLayoutPreference;
+ @Mock
+ private ImageView mMockHeaderIcon;
+
+ @Mock
+ private ActionButtonsPreference mMockButtonsPref;
+ @Mock
+ private Preference mMockSignalStrengthPref;
+ @Mock
+ private Preference mMockTxLinkSpeedPref;
+ @Mock
+ private Preference mMockRxLinkSpeedPref;
+ @Mock
+ private Preference mMockFrequencyPref;
+ @Mock
+ private Preference mMockSecurityPref;
+ @Mock
+ private Preference mMockSsidPref;
+ @Mock
+ private Preference mMockMacAddressPref;
+ @Mock
+ private Preference mMockIpAddressPref;
+ @Mock
+ private Preference mMockGatewayPref;
+ @Mock
+ private Preference mMockSubnetPref;
+ @Mock
+ private Preference mMockDnsPref;
+ @Mock
+ private PreferenceCategory mMockIpv6Category;
+ @Mock
+ private Preference mMockIpv6AddressesPref;
+ @Mock
+ private PackageManager mMockPackageManager;
+
+ @Captor
+ private ArgumentCaptor<NetworkCallback> mCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<View.OnClickListener> mForgetClickListener;
+
+ private Context mContext;
+ private Lifecycle mLifecycle;
+ private LifecycleOwner mLifecycleOwner;
+ private LinkProperties mLinkProperties;
+ private WifiDetailPreferenceController2 mController;
+
+ // This class exists so that these values can be made static final. They can't be static final
+ // members of the test class, because any attempt to call IpPrefix or RouteInfo constructors
+ // during static initialization of the test class results in NoSuchMethorError being thrown
+ // when the test is run.
+ private static class Constants {
+ static final int IPV4_PREFIXLEN = 25;
+ static final LinkAddress IPV4_ADDR;
+ static final Inet4Address IPV4_GATEWAY;
+ static final RouteInfo IPV4_DEFAULT;
+ static final RouteInfo IPV4_SUBNET;
+ static final LinkAddress IPV6_LINKLOCAL;
+ static final LinkAddress IPV6_GLOBAL1;
+ static final LinkAddress IPV6_GLOBAL2;
+ static final InetAddress IPV4_DNS1;
+ static final InetAddress IPV4_DNS2;
+ static final InetAddress IPV6_DNS;
+
+ private static LinkAddress ipv6LinkAddress(String addr) throws UnknownHostException {
+ return new LinkAddress(InetAddress.getByName(addr), 64);
+ }
+
+ private static LinkAddress ipv4LinkAddress(String addr, int prefixlen)
+ throws UnknownHostException {
+ return new LinkAddress(InetAddress.getByName(addr), prefixlen);
+ }
+
+ static {
+ try {
+ // We create our test constants in these roundabout ways because the robolectric
+ // shadows don't contain NetworkUtils.parseNumericAddress and other utility methods,
+ // so the easy ways to do things fail with NoSuchMethodError.
+ IPV4_ADDR = ipv4LinkAddress("192.0.2.2", IPV4_PREFIXLEN);
+ IPV4_GATEWAY = (Inet4Address) InetAddress.getByName("192.0.2.127");
+
+ final Inet4Address any4 = (Inet4Address) InetAddress.getByName("0.0.0.0");
+ IpPrefix subnet = new IpPrefix(IPV4_ADDR.getAddress(), IPV4_PREFIXLEN);
+ IPV4_SUBNET = new RouteInfo(subnet, any4);
+ IPV4_DEFAULT = new RouteInfo(new IpPrefix(any4, 0), IPV4_GATEWAY);
+
+ IPV6_LINKLOCAL = ipv6LinkAddress("fe80::211:25ff:fef8:7cb2%1");
+ IPV6_GLOBAL1 = ipv6LinkAddress("2001:db8:1::211:25ff:fef8:7cb2");
+ IPV6_GLOBAL2 = ipv6LinkAddress("2001:db8:1::3dfe:8902:f98f:739d");
+
+ IPV4_DNS1 = InetAddress.getByName("8.8.8.8");
+ IPV4_DNS2 = InetAddress.getByName("8.8.4.4");
+ IPV6_DNS = InetAddress.getByName("2001:4860:4860::64");
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Invalid hardcoded IP addresss: " + e);
+ }
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+
+ when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockAccessPoint.getConfig()).thenReturn(mMockWifiConfig);
+ when(mMockAccessPoint.getLevel()).thenReturn(LEVEL);
+ when(mMockAccessPoint.getSecurityString(false)).thenReturn(SECURITY);
+ when(mMockAccessPoint.getSsidStr()).thenReturn(SSID);
+ when(mMockConnectivityManager.getNetworkInfo(any(Network.class)))
+ .thenReturn(mMockNetworkInfo);
+ doNothing().when(mMockConnectivityManager).registerNetworkCallback(
+ nullable(NetworkRequest.class), mCallbackCaptor.capture(), nullable(Handler.class));
+ mMockButtonsPref = createMock();
+ when(mMockButtonsPref.setButton1OnClickListener(mForgetClickListener.capture()))
+ .thenReturn(mMockButtonsPref);
+
+ when(mMockWifiInfo.getTxLinkSpeedMbps()).thenReturn(TX_LINK_SPEED);
+ when(mMockWifiInfo.getRxLinkSpeedMbps()).thenReturn(RX_LINK_SPEED);
+ when(mMockWifiInfo.getRssi()).thenReturn(RSSI);
+ when(mMockWifiInfo.getMacAddress()).thenReturn(MAC_ADDRESS);
+ when(mMockWifiManager.getConnectionInfo()).thenReturn(mMockWifiInfo);
+
+ when(mMockWifiManager.getCurrentNetwork()).thenReturn(mMockNetwork);
+ mLinkProperties = new LinkProperties();
+ when(mMockConnectivityManager.getLinkProperties(mMockNetwork)).thenReturn(mLinkProperties);
+
+ when(mMockFragment.getActivity()).thenReturn(mMockActivity);
+
+ ShadowEntityHeaderController.setUseMock(mMockHeaderController);
+ // builder pattern
+ when(mMockHeaderController.setRecyclerView(mMockFragment.getListView(), mLifecycle))
+ .thenReturn(mMockHeaderController);
+ when(mMockHeaderController.setSummary(anyString())).thenReturn(mMockHeaderController);
+ when(mMockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
+
+ setupMockedPreferenceScreen();
+ }
+
+ private void setUpForConnectedNetwork() {
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ ArrayList list = new ArrayList<>();
+ list.add(mMockAccessPoint);
+ when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+ WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(true);
+ when(mMockAccessPoint.isReachable()).thenReturn(true);
+
+ mController = newWifiDetailPreferenceController2();
+ }
+
+ private void setUpForDisconnectedNetwork() {
+ when(mMockAccessPoint.isActive()).thenReturn(false);
+ ArrayList list = new ArrayList<>();
+ list.add(mMockAccessPoint);
+ when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+ WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(true);
+ when(mMockAccessPoint.isReachable()).thenReturn(true);
+
+ mController = newWifiDetailPreferenceController2();
+ }
+
+ private void setUpForNotInRangeNetwork() {
+ when(mMockAccessPoint.isActive()).thenReturn(false);
+ ArrayList list = new ArrayList<>();
+ list.add(mMockAccessPoint);
+ when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+ WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(false);
+ when(mMockAccessPoint.isReachable()).thenReturn(false);
+
+ mController = newWifiDetailPreferenceController2();
+ }
+
+ private WifiDetailPreferenceController2 newWifiDetailPreferenceController2() {
+ return new WifiDetailPreferenceController2(
+ mMockAccessPoint,
+ mMockConnectivityManager,
+ mContext,
+ mMockFragment,
+ null, // Handler
+ mLifecycle,
+ mMockWifiManager,
+ mMockMetricsFeatureProvider,
+ mMockIconInjector);
+ }
+
+ private void setupMockedPreferenceScreen() {
+ when(mMockScreen.getPreferenceManager().getContext()).thenReturn(mContext);
+
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_HEADER))
+ .thenReturn(mMockHeaderLayoutPreference);
+ when(mMockHeaderLayoutPreference.findViewById(R.id.entity_header_icon))
+ .thenReturn(mMockHeaderIcon);
+
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_BUTTONS_PREF))
+ .thenReturn(mMockButtonsPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SIGNAL_STRENGTH_PREF))
+ .thenReturn(mMockSignalStrengthPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_TX_LINK_SPEED))
+ .thenReturn(mMockTxLinkSpeedPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_RX_LINK_SPEED))
+ .thenReturn(mMockRxLinkSpeedPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_FREQUENCY_PREF))
+ .thenReturn(mMockFrequencyPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SECURITY_PREF))
+ .thenReturn(mMockSecurityPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SSID_PREF))
+ .thenReturn(mMockSsidPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_MAC_ADDRESS_PREF))
+ .thenReturn(mMockMacAddressPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IP_ADDRESS_PREF))
+ .thenReturn(mMockIpAddressPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_GATEWAY_PREF))
+ .thenReturn(mMockGatewayPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SUBNET_MASK_PREF))
+ .thenReturn(mMockSubnetPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_DNS_PREF))
+ .thenReturn(mMockDnsPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IPV6_CATEGORY))
+ .thenReturn(mMockIpv6Category);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IPV6_ADDRESSES_PREF))
+ .thenReturn(mMockIpv6AddressesPref);
+ }
+
+ private void displayAndResume() {
+ mController.displayPreference(mMockScreen);
+ mController.onResume();
+ }
+
+ @Test
+ public void isAvailable_shouldAlwaysReturnTrue() {
+ setUpForConnectedNetwork();
+ mController.displayPreference(mMockScreen);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void securityPreference_stringShouldBeSet() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockSecurityPref).setSummary(SECURITY);
+ }
+
+ @Test
+ public void latestWifiInfo_shouldBeFetchedInDisplayPreferenceForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+ }
+
+ @Test
+ public void latestWifiInfo_shouldNotBeFetchedInDisplayPreferenceForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockWifiManager, never()).getConnectionInfo();
+ }
+
+ @Test
+ public void latestWifiInfo_shouldNotBeFetchedInDisplayPreferenceForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockWifiManager, never()).getConnectionInfo();
+ }
+
+ @Test
+ public void latestNetworkInfo_shouldBeFetchedInDisplayPreferenceForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ }
+
+ @Test
+ public void latestNetworkInfo_shouldNotBeFetchedInDisplayPreferenceForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, never()).getNetworkInfo(any(Network.class));
+ }
+
+ @Test
+ public void latestNetworkInfo_shouldNotBeFetchedInDisplayPreferenceForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, never()).getNetworkInfo(any(Network.class));
+ }
+
+ @Test
+ public void networkCallback_shouldBeRegisteredOnResume() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).registerNetworkCallback(
+ nullable(NetworkRequest.class), mCallbackCaptor.capture(), nullable(Handler.class));
+ }
+
+ @Test
+ public void networkCallback_shouldBeUnregisteredOnPause() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+ mController.onPause();
+
+ verify(mMockConnectivityManager, times(1))
+ .unregisterNetworkCallback(mCallbackCaptor.getValue());
+ }
+
+ @Test
+ public void entityHeader_shouldHaveIconSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setIcon(expectedIcon);
+ }
+
+ @Test
+ public void entityHeader_shouldHaveIconSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setIcon(expectedIcon);
+ }
+
+ @Test
+ public void entityHeader_shouldNotHaveIconSetForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockHeaderController, never()).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void entityHeader_shouldHaveLabelSetToTitle() {
+ setUpForConnectedNetwork();
+ String label = "title";
+ when(mMockAccessPoint.getTitle()).thenReturn(label);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setLabel(label);
+ }
+
+ @Test
+ public void entityHeader_shouldHaveSummarySet() {
+ setUpForConnectedNetwork();
+ String summary = "summary";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setSummary(summary);
+ }
+
+ @Test
+ public void entityHeader_shouldConvertSavedAsDisconnected() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockAccessPoint, times(1)).getSettingsSummary(true /*convertSavedAsDisconnected*/);
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveIconSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveIconSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void signalStrengthPref_shouldNotHaveIconSetForOutOfRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref, never()).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveDetailTextSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ String expectedStrength =
+ mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setSummary(expectedStrength);
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveDetailTextSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ String expectedStrength =
+ mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setSummary(expectedStrength);
+ }
+
+ @Test
+ public void signalStrengthPref_shouldNotHaveDetailTextSetForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void linkSpeedPref_shouldNotShowIfNotSet() {
+ setUpForConnectedNetwork();
+ when(mMockWifiInfo.getTxLinkSpeedMbps()).thenReturn(WifiInfo.LINK_SPEED_UNKNOWN);
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(false);
+ }
+
+ @Test
+ public void linkSpeedPref_shouldVisibleForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ String expectedLinkSpeed = mContext.getString(R.string.tx_link_speed, TX_LINK_SPEED);
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(true);
+ verify(mMockTxLinkSpeedPref).setSummary(expectedLinkSpeed);
+ }
+
+ @Test
+ public void linkSpeedPref_shouldInvisibleForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(false);
+ verify(mMockTxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void linkSpeedPref_shouldInvisibleForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(false);
+ verify(mMockTxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldNotShowIfNotSet() {
+ setUpForConnectedNetwork();
+ when(mMockWifiInfo.getRxLinkSpeedMbps()).thenReturn(WifiInfo.LINK_SPEED_UNKNOWN);
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(false);
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldVisibleForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ String expectedLinkSpeed = mContext.getString(R.string.rx_link_speed, RX_LINK_SPEED);
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(true);
+ verify(mMockRxLinkSpeedPref).setSummary(expectedLinkSpeed);
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldInvisibleForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(false);
+ verify(mMockRxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldInvisibleForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(false);
+ verify(mMockRxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void ssidPref_shouldHaveDetailTextSetForPasspointR1() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(true);
+ when(mMockAccessPoint.isOsuProvider()).thenReturn(false);
+
+ displayAndResume();
+
+ verify(mMockSsidPref, times(1)).setSummary(SSID);
+ verify(mMockSsidPref, times(1)).setVisible(true);
+ }
+
+ @Test
+ public void ssidPref_shouldHaveDetailTextSetForPasspointR2() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(false);
+ when(mMockAccessPoint.isOsuProvider()).thenReturn(true);
+
+ displayAndResume();
+
+ verify(mMockSsidPref, times(1)).setSummary(SSID);
+ verify(mMockSsidPref, times(1)).setVisible(true);
+ }
+
+ @Test
+ public void ssidPref_shouldNotShowIfNotPasspoint() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(false);
+ when(mMockAccessPoint.isOsuProvider()).thenReturn(false);
+
+ displayAndResume();
+
+ verify(mMockSsidPref).setVisible(false);
+ }
+
+ @Test
+ public void macAddressPref_shouldVisibleForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setVisible(true);
+ verify(mMockMacAddressPref).setSummary(MAC_ADDRESS);
+ }
+
+ @Test
+ public void macAddressPref_shouldVisibleAsRandomizedForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ when(mMockWifiConfig.getRandomizedMacAddress()).thenReturn(mMockMacAddress);
+ when(mMockMacAddress.toString()).thenReturn(RANDOMIZED_MAC_ADDRESS);
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setVisible(true);
+ verify(mMockMacAddressPref).setSummary(RANDOMIZED_MAC_ADDRESS);
+ }
+
+ @Test
+ public void macAddressPref_shouldVisibleAsFactoryForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+ when(mMockWifiManager.getFactoryMacAddresses())
+ .thenReturn(new String[]{FACTORY_MAC_ADDRESS});
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setVisible(true);
+ verify(mMockMacAddressPref).setSummary(FACTORY_MAC_ADDRESS);
+ }
+
+ @Test
+ public void ipAddressPref_shouldHaveDetailTextSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
+
+ displayAndResume();
+
+ verify(mMockIpAddressPref).setSummary(Constants.IPV4_ADDR.getAddress().getHostAddress());
+ verify(mMockIpAddressPref).setVisible(true);
+ }
+
+ @Test
+ public void ipAddressPref_shouldInvisibleForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockIpAddressPref).setVisible(false);
+ }
+
+ @Test
+ public void gatewayAndSubnet_shouldHaveDetailTextSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
+ mLinkProperties.addRoute(Constants.IPV4_DEFAULT);
+ mLinkProperties.addRoute(Constants.IPV4_SUBNET);
+
+ displayAndResume();
+
+ verify(mMockSubnetPref).setSummary("255.255.255.128");
+ verify(mMockGatewayPref).setSummary("192.0.2.127");
+ verify(mMockSubnetPref).setVisible(true);
+ }
+
+ @Test
+ public void gatewayAndSubnet_shouldInvisibleSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockSubnetPref).setVisible(false);
+ }
+
+ @Test
+ public void dnsServersPref_shouldHaveDetailTextSetForConnectedNetwork()
+ throws UnknownHostException {
+ setUpForConnectedNetwork();
+ mLinkProperties.addDnsServer(InetAddress.getByAddress(new byte[] {8, 8, 4, 4}));
+ mLinkProperties.addDnsServer(InetAddress.getByAddress(new byte[] {8, 8, 8, 8}));
+ mLinkProperties.addDnsServer(Constants.IPV6_DNS);
+
+ displayAndResume();
+
+ verify(mMockDnsPref).setSummary(
+ "8.8.4.4\n" + "8.8.8.8\n" + Constants.IPV6_DNS.getHostAddress());
+ verify(mMockDnsPref).setVisible(true);
+ }
+
+ @Test
+ public void dnsServersPref_shouldInvisibleSetForDisconnectedNetwork()
+ throws UnknownHostException {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockDnsPref).setVisible(false);
+ }
+
+ @Test
+ public void noCurrentNetwork_shouldNotFinishActivityForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ when(mMockWifiManager.getCurrentNetwork()).thenReturn(null);
+
+ displayAndResume();
+
+ verify(mMockActivity, never()).finish();
+ }
+
+ @Test
+ public void noLinkProperties_allIpDetailsHidden() {
+ setUpForConnectedNetwork();
+ when(mMockConnectivityManager.getLinkProperties(mMockNetwork)).thenReturn(null);
+ reset(mMockIpv6Category, mMockIpAddressPref, mMockSubnetPref, mMockGatewayPref,
+ mMockDnsPref);
+
+ displayAndResume();
+
+ verify(mMockIpv6Category).setVisible(false);
+ verify(mMockIpAddressPref).setVisible(false);
+ verify(mMockSubnetPref).setVisible(false);
+ verify(mMockGatewayPref).setVisible(false);
+ verify(mMockDnsPref).setVisible(false);
+ verify(mMockIpv6Category, never()).setVisible(true);
+ verify(mMockIpAddressPref, never()).setVisible(true);
+ verify(mMockSubnetPref, never()).setVisible(true);
+ verify(mMockGatewayPref, never()).setVisible(true);
+ verify(mMockDnsPref, never()).setVisible(true);
+ }
+
+ @Test
+ public void disconnectedNetwork_allIpDetailsHidden() {
+ setUpForDisconnectedNetwork();
+ reset(mMockIpv6Category, mMockIpAddressPref, mMockSubnetPref, mMockGatewayPref,
+ mMockDnsPref);
+
+ displayAndResume();
+
+ verify(mMockIpv6Category).setVisible(false);
+ verify(mMockIpAddressPref).setVisible(false);
+ verify(mMockSubnetPref).setVisible(false);
+ verify(mMockGatewayPref).setVisible(false);
+ verify(mMockDnsPref).setVisible(false);
+ verify(mMockIpv6Category, never()).setVisible(true);
+ verify(mMockIpAddressPref, never()).setVisible(true);
+ verify(mMockSubnetPref, never()).setVisible(true);
+ verify(mMockGatewayPref, never()).setVisible(true);
+ verify(mMockDnsPref, never()).setVisible(true);
+ }
+
+ // Convenience method to convert a LinkAddress to a string without a prefix length.
+ private String asString(LinkAddress l) {
+ return l.getAddress().getHostAddress();
+ }
+
+ // Pretend that the NetworkCallback was triggered with a new copy of lp. We need to create a
+ // new copy because the code only updates if !mLinkProperties.equals(lp).
+ private void updateLinkProperties(LinkProperties lp) {
+ mCallbackCaptor.getValue().onLinkPropertiesChanged(mMockNetwork, new LinkProperties(lp));
+ }
+
+ private void updateNetworkCapabilities(NetworkCapabilities nc) {
+ mCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, new NetworkCapabilities(nc));
+ }
+
+ private NetworkCapabilities makeNetworkCapabilities() {
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.clearAll();
+ nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ return nc;
+ }
+
+ private void verifyDisplayedIpv6Addresses(InOrder inOrder, LinkAddress... addresses) {
+ String text = Arrays.stream(addresses)
+ .map(address -> asString(address))
+ .collect(Collectors.joining("\n"));
+ inOrder.verify(mMockIpv6AddressesPref).setSummary(text);
+ }
+
+ @Test
+ public void onLinkPropertiesChanged_updatesFields() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ InOrder inOrder = inOrder(mMockIpAddressPref, mMockGatewayPref, mMockSubnetPref,
+ mMockDnsPref, mMockIpv6Category, mMockIpv6AddressesPref);
+
+ LinkProperties lp = new LinkProperties();
+
+ lp.addLinkAddress(Constants.IPV6_LINKLOCAL);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL);
+ inOrder.verify(mMockIpv6Category).setVisible(true);
+
+ lp.addRoute(Constants.IPV4_DEFAULT);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockGatewayPref).setSummary(Constants.IPV4_GATEWAY.getHostAddress());
+ inOrder.verify(mMockGatewayPref).setVisible(true);
+
+ lp.addLinkAddress(Constants.IPV4_ADDR);
+ lp.addRoute(Constants.IPV4_SUBNET);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockIpAddressPref).setSummary(asString(Constants.IPV4_ADDR));
+ inOrder.verify(mMockIpAddressPref).setVisible(true);
+ inOrder.verify(mMockSubnetPref).setSummary("255.255.255.128");
+ inOrder.verify(mMockSubnetPref).setVisible(true);
+
+ lp.addLinkAddress(Constants.IPV6_GLOBAL1);
+ lp.addLinkAddress(Constants.IPV6_GLOBAL2);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder,
+ Constants.IPV6_LINKLOCAL,
+ Constants.IPV6_GLOBAL1,
+ Constants.IPV6_GLOBAL2);
+
+ lp.removeLinkAddress(Constants.IPV6_GLOBAL1);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder,
+ Constants.IPV6_LINKLOCAL,
+ Constants.IPV6_GLOBAL2);
+
+ lp.addDnsServer(Constants.IPV6_DNS);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockDnsPref).setSummary(Constants.IPV6_DNS.getHostAddress());
+ inOrder.verify(mMockDnsPref).setVisible(true);
+
+ lp.addDnsServer(Constants.IPV4_DNS1);
+ lp.addDnsServer(Constants.IPV4_DNS2);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockDnsPref).setSummary(
+ Constants.IPV6_DNS.getHostAddress() + "\n"
+ + Constants.IPV4_DNS1.getHostAddress() + "\n"
+ + Constants.IPV4_DNS2.getHostAddress());
+ inOrder.verify(mMockDnsPref).setVisible(true);
+ }
+
+ @Test
+ public void onCapabilitiesChanged_callsRefreshIfNecessary() {
+ setUpForConnectedNetwork();
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ when(mMockConnectivityManager.getNetworkCapabilities(mMockNetwork))
+ .thenReturn(new NetworkCapabilities(nc));
+
+ String summary = "Connected, no Internet";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+
+ InOrder inOrder = inOrder(mMockHeaderController);
+ displayAndResume();
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // Check that an irrelevant capability update does not update the access point summary, as
+ // doing so could cause unnecessary jank...
+ summary = "Connected";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController, never()).setSummary(any(CharSequence.class));
+
+ // ... but that if the network validates, then we do refresh.
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ summary = "Connected, no Internet";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+
+ // Another irrelevant update won't cause the UI to refresh...
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController, never()).setSummary(any(CharSequence.class));
+
+ // ... but if the network is no longer validated, then we display "connected, no Internet".
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // UI will be refreshed when private DNS is broken.
+ summary = "Private DNS server cannot be accessed";
+ when(mMockAccessPoint.getSettingsSummary(true /* convertSavedAsDisconnected */))
+ .thenReturn(summary);
+ nc.setPrivateDnsBroken(true);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // UI will be refreshed when device connects to a partial connectivity network.
+ summary = "Limited connection";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // Although UI will be refreshed when network become validated. The Settings should
+ // continue to display "Limited connection" if network still provides partial connectivity.
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+ }
+
+ @Test
+ public void canForgetNetwork_shouldInvisibleIfWithoutConfiguration() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.getConfig()).thenReturn(null);
+ mController = newWifiDetailPreferenceController2();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(false);
+ }
+
+ @Test
+ public void canForgetNetwork_ephemeral() {
+ setUpForConnectedNetwork();
+ when(mMockWifiInfo.isEphemeral()).thenReturn(true);
+ when(mMockAccessPoint.getConfig()).thenReturn(null);
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(true);
+ }
+
+ @Test
+ public void canForgetNetwork_saved() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(true);
+ }
+
+ @Test
+ public void canForgetNetwork_lockedDown() {
+ setUpForConnectedNetwork();
+ lockDownNetwork();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(false);
+ }
+
+ @Test
+ public void canShareNetwork_shouldInvisibleIfWithoutConfiguration() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.getConfig()).thenReturn(null);
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton4Visible(false);
+ }
+
+ @Test
+ public void canModifyNetwork_saved() {
+ setUpForConnectedNetwork();
+ assertThat(mController.canModifyNetwork()).isTrue();
+ }
+
+ @Test
+ public void canModifyNetwork_lockedDown() {
+ setUpForConnectedNetwork();
+ lockDownNetwork();
+
+ assertThat(mController.canModifyNetwork()).isFalse();
+ }
+
+ /**
+ * Pretends that current network is locked down by device owner.
+ */
+ private void lockDownNetwork() {
+ final int doUserId = 123;
+ final int doUid = 1234;
+ String doPackage = "some.package";
+
+ mMockWifiConfig.creatorUid = doUid;
+ ComponentName doComponent = new ComponentName(doPackage, "some.Class");
+ try {
+ when(mMockPackageManager.getPackageUidAsUser(Matchers.anyString(), Matchers.anyInt()))
+ .thenReturn(doUid);
+ } catch (PackageManager.NameNotFoundException e) {
+ //do nothing
+ }
+ ShadowDevicePolicyManager.getShadow().setDeviceOwnerComponentOnAnyUser(doComponent);
+ ShadowDevicePolicyManager.getShadow().setDeviceOwnerUserId(doUserId);
+
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 1);
+ }
+
+ @Test
+ public void forgetNetwork_ephemeral() {
+ setUpForConnectedNetwork();
+ String ssid = "ssid";
+ when(mMockWifiInfo.isEphemeral()).thenReturn(true);
+ when(mMockWifiInfo.getSSID()).thenReturn(ssid);
+
+ displayAndResume();
+ mForgetClickListener.getValue().onClick(null);
+
+ verify(mMockWifiManager).disableEphemeralNetwork(ssid);
+ verify(mMockMetricsFeatureProvider)
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
+ }
+
+ @Test
+ public void forgetNetwork_saved() {
+ setUpForConnectedNetwork();
+ mMockWifiConfig.networkId = 5;
+
+ mController.displayPreference(mMockScreen);
+ mForgetClickListener.getValue().onClick(null);
+
+ verify(mMockWifiManager).forget(mMockWifiConfig.networkId, null);
+ verify(mMockMetricsFeatureProvider)
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
+ }
+
+ @Test
+ public void forgetNetwork_shouldShowDialog() {
+ setUpForConnectedNetwork();
+ final WifiDetailPreferenceController2 spyController = spy(mController);
+
+ mMockWifiConfig.networkId = 5;
+ when(mMockAccessPoint.isPasspoint()).thenReturn(true);
+ when(mMockAccessPoint.getPasspointFqdn()).thenReturn(FQDN);
+ spyController.displayPreference(mMockScreen);
+
+ mForgetClickListener.getValue().onClick(null);
+
+ verify(mMockWifiManager, times(0)).removePasspointConfiguration(FQDN);
+ verify(mMockMetricsFeatureProvider, times(0))
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
+ verify(spyController).showConfirmForgetDialog();
+ }
+
+ @Test
+ public void networkStateChangedIntent_shouldRefetchInfo() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void networkStateChangedIntent_shouldRefetchInfoForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void rssiChangedIntent_shouldRefetchInfo() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void rssiChangedIntent_shouldRefetchInfoForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void networkDisconnectedState_shouldNotFinishActivityForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ when(mMockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(null);
+ mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+
+ verify(mMockActivity, never()).finish();
+ }
+
+ @Test
+ public void networkOnLost_shouldNotFinishActivityForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ mCallbackCaptor.getValue().onLost(mMockNetwork);
+
+ verify(mMockActivity, never()).finish();
+ }
+
+ @Test
+ public void ipv6AddressPref_shouldHaveHostAddressTextSet() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV6_LINKLOCAL);
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL1);
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
+
+ displayAndResume();
+
+ String expectedAddresses = String.join("\n",
+ asString(Constants.IPV6_LINKLOCAL),
+ asString(Constants.IPV6_GLOBAL1),
+ asString(Constants.IPV6_GLOBAL2));
+
+ verify(mMockIpv6AddressesPref).setSummary(expectedAddresses);
+ }
+
+ @Test
+ public void ipv6AddressPref_shouldNotBeSelectable() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
+
+ displayAndResume();
+
+ assertThat(mMockIpv6AddressesPref.isSelectable()).isFalse();
+ }
+
+ @Test
+ public void captivePortal_shouldShowSignInButton() {
+ setUpForConnectedNetwork();
+
+ InOrder inOrder = inOrder(mMockButtonsPref);
+
+ displayAndResume();
+
+ inOrder.verify(mMockButtonsPref).setButton2Visible(false);
+
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockButtonsPref).setButton2Visible(false);
+
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockButtonsPref).setButton2Visible(true);
+
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockButtonsPref).setButton2Visible(false);
+ }
+
+ @Test
+ public void testSignInButton_shouldStartCaptivePortalApp() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ ArgumentCaptor<OnClickListener> captor = ArgumentCaptor.forClass(OnClickListener.class);
+ verify(mMockButtonsPref).setButton2OnClickListener(captor.capture());
+ captor.getValue().onClick(null);
+ verify(mMockConnectivityManager).startCaptivePortalApp(mMockNetwork);
+ verify(mMockMetricsFeatureProvider)
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_SIGNIN);
+ }
+
+ @Test
+ public void testSignInButton_shouldHideSignInButtonForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ when(mMockConnectivityManager.getNetworkCapabilities(mMockNetwork))
+ .thenReturn(new NetworkCapabilities(nc));
+
+ // verify onResume
+ displayAndResume();
+
+ verify(mMockButtonsPref, never()).setButton2Visible(true);
+ verify(mMockButtonsPref).setButton2Visible(false);
+
+ // verify onCapabilitiesChanged
+ updateNetworkCapabilities(nc);
+
+ verify(mMockButtonsPref, never()).setButton2Visible(true);
+ verify(mMockButtonsPref).setButton2Visible(false);
+ }
+
+ @Test
+ public void testConnectButton_shouldInvisibleForConnectNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref, times(1)).setButton3Visible(false);
+ }
+
+ @Test
+ public void testConnectButton_shouldVisibleForDisconnectNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect);
+ }
+
+ private void setUpForToast() {
+ Resources res = mContext.getResources();
+ when(mMockActivity.getResources()).thenReturn(res);
+ }
+
+ @Test
+ public void testConnectButton_clickConnect_displayAsSuccess() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ String label = "title";
+ when(mMockAccessPoint.getTitle()).thenReturn(label);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as connected
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ mController.updateAccessPoint();
+
+ // check connect button invisible, be init as default state and toast success message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(false);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_connected_to_message, label));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectButFailed_displayFailMessage() {
+ setUpForDisconnectedNetwork();
+ ArgumentCaptor<WifiManager.ActionListener> connectListenerCaptor =
+ ArgumentCaptor.forClass(WifiManager.ActionListener.class);
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), connectListenerCaptor.capture());
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ connectListenerCaptor.getValue().onFailure(-1);
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ private void verifyConnectBtnSetUpAsVisible(InOrder inOrder) {
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Icon(R.drawable.ic_settings_wireless);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ }
+
+ private void verifyConnectBtnSetUpAsConnecting(InOrder inOrder) {
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connecting);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Enabled(false);
+ }
+
+ private void verifyConnectBtnBeInitAsDefault(InOrder inOrder) {
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Icon(R.drawable.ic_settings_wireless);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Enabled(true);
+ }
+
+ @Test
+ public void testConnectButton_clickConnectButTimeout_displayFailMessage() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectButTimeout_displayNotInRangeMessage() {
+ setUpForNotInRangeNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_not_in_range_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectWhenWiFiDisabled_displaySuccessMessage() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ String label = "title";
+ when(mMockAccessPoint.getTitle()).thenReturn(label);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify Wi-Fi enabled
+ mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
+
+ // check had connect network and icon display as expected
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as connected
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ mController.updateAccessPoint();
+
+ // check connect button invisible, be init as default state and toast success message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(false);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_connected_to_message, label));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectWhenWiFiDisabled_failedToConnectWiFi() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify Wi-Fi enabled
+ mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
+
+ // check had connect network and icon display as expected
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ @Test
+ public void
+ testConnectButton_clickConnectWhenWiFiDisabled_failedToConnectWifiBecauseNotInRange() {
+ setUpForNotInRangeNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify Wi-Fi enabled
+ mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
+
+ // check had connect network and icon display as expected
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_not_in_range_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectWhenWiFiDisabled_failedToEnableWifi() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify turn on Wi-Fi failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectAndBackKey_ignoreTimeoutEvent() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // leave detail page
+ when(mMockFragment.getActivity()).thenReturn(null);
+
+ // timeout happened
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ inOrder.verify(mMockButtonsPref, never()).setButton3Text(R.string.wifi_connect);
+ inOrder.verify(mMockButtonsPref, never()).setButton3Icon(R.drawable.ic_settings_wireless);
+ inOrder.verify(mMockButtonsPref, never()).setButton3Enabled(true);
+ inOrder.verify(mMockButtonsPref, never()).setButton3Visible(true);
+ assertThat(ShadowToast.shownToastCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void updateAccessPoint_returnFalseForNothingChanged() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isFalse();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForSignalLevelChanged() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ // Level changed
+ when(mMockAccessPoint.getLevel()).thenReturn(LEVEL + 1);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsNotInRange() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ // change as not in range
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(false);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsInRange() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ // change as in range
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(true);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsConnected() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ // change as connected
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsDisconnected() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ // change as disconnected
+ when(mMockAccessPoint.isActive()).thenReturn(false);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForAccessPointUpdated() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ // change as disconnected
+ when(mMockAccessPoint.update(mMockWifiConfig, mMockWifiInfo, mMockNetworkInfo))
+ .thenReturn(true);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void testRefreshRssiViews_shouldNotUpdateIfLevelIsSameForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockAccessPoint, times(3)).getLevel();
+ verify(mMockIconInjector, times(1)).getIcon(anyInt());
+ }
+
+ @Test
+ public void testRefreshRssiViews_shouldUpdateOnLevelChangeForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ when(mMockAccessPoint.getLevel()).thenReturn(0);
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockAccessPoint, times(4)).getLevel();
+ verify(mMockIconInjector, times(2)).getIcon(anyInt());
+ }
+
+ @Test
+ public void testRefreshRssiViews_shouldNotUpdateForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ when(mMockAccessPoint.getLevel()).thenReturn(0);
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockSignalStrengthPref, times(2)).setVisible(false);
+ }
+
+ @Test
+ public void testRedrawIconForHeader_shouldEnlarge() {
+ setUpForConnectedNetwork();
+ ArgumentCaptor<BitmapDrawable> drawableCaptor =
+ ArgumentCaptor.forClass(BitmapDrawable.class);
+ Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
+ when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+
+ displayAndResume();
+
+ verify(mMockHeaderController, times(1)).setIcon(drawableCaptor.capture());
+
+ int expectedSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.wifi_detail_page_header_image_size);
+ BitmapDrawable icon = drawableCaptor.getValue();
+ assertThat(icon.getMinimumWidth()).isEqualTo(expectedSize);
+ assertThat(icon.getMinimumHeight()).isEqualTo(expectedSize);
+ }
+
+ @Test
+ public void testRedrawIconForHeader_shouldEnlargeForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ ArgumentCaptor<BitmapDrawable> drawableCaptor =
+ ArgumentCaptor.forClass(BitmapDrawable.class);
+ Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
+ when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+
+ displayAndResume();
+
+ verify(mMockHeaderController, times(1)).setIcon(drawableCaptor.capture());
+
+ int expectedSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.wifi_detail_page_header_image_size);
+ BitmapDrawable icon = drawableCaptor.getValue();
+ assertThat(icon.getMinimumWidth()).isEqualTo(expectedSize);
+ assertThat(icon.getMinimumHeight()).isEqualTo(expectedSize);
+ }
+
+ @Test
+ public void testRedrawIconForHeader_shouldNotEnlargeIfNotVectorDrawable() {
+ setUpForConnectedNetwork();
+ ArgumentCaptor<ColorDrawable> drawableCaptor =
+ ArgumentCaptor.forClass(ColorDrawable.class);
+
+ displayAndResume();
+
+ verify(mMockHeaderController, times(1)).setIcon(drawableCaptor.capture());
+ ColorDrawable icon = drawableCaptor.getValue();
+ assertThat(icon).isNotNull();
+ }
+
+ @Test
+ public void checkMacTitle_whenPrivacyRandomizedMac_shouldBeRandom() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ when(mMockWifiConfig.getRandomizedMacAddress()).thenReturn(mMockMacAddress);
+ when(mMockMacAddress.toString()).thenReturn(RANDOMIZED_MAC_ADDRESS);
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setTitle(R.string.wifi_advanced_randomized_mac_address_title);
+ }
+
+ @Test
+ public void checkMacTitle_whenPrivacyDeviceMac_shouldBeFactory() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+ when(mMockWifiConfig.getRandomizedMacAddress()).thenReturn(mMockMacAddress);
+ when(mMockWifiManager.getFactoryMacAddresses())
+ .thenReturn(new String[]{FACTORY_MAC_ADDRESS});
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setTitle(R.string.wifi_advanced_device_mac_address_title);
+ }
+
+ @Test
+ @Ignore
+ public void entityHeader_expiredPasspointR1_shouldHandleExpiration() {
+ setUpForDisconnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(true);
+ when(mMockAccessPoint.isPasspointConfigurationR1()).thenReturn(true);
+ when(mMockAccessPoint.isExpired()).thenReturn(true);
+ String expireSummary = mContext.getResources().getString(
+ com.android.settingslib.R.string.wifi_passpoint_expired);
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton3Visible(false);
+ verify(mMockHeaderController).setSummary(expireSummary);
+ }
+
+ private ActionButtonsPreference createMock() {
+ final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
+ when(pref.setButton1Text(anyInt())).thenReturn(pref);
+ when(pref.setButton1Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton2Text(anyInt())).thenReturn(pref);
+ when(pref.setButton2Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton3Text(anyInt())).thenReturn(pref);
+ when(pref.setButton3Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton4Text(anyInt())).thenReturn(pref);
+ when(pref.setButton4Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ return pref;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java
new file mode 100644
index 0000000..517c96a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+
+import androidx.preference.DropDownPreference;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiMeteredPreferenceController2Test {
+
+ private static final int METERED_OVERRIDE_NONE = 0;
+ private static final int METERED_OVERRIDE_METERED = 1;
+ private static final int METERED_OVERRIDE_NOT_METERED = 2;
+
+ @Mock
+ private WifiConfiguration mWifiConfiguration;
+
+ private WifiMeteredPreferenceController2 mPreferenceController;
+ private Context mContext;
+ private DropDownPreference mDropDownPreference;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+
+ mPreferenceController = spy(
+ new WifiMeteredPreferenceController2(mContext, mWifiConfiguration));
+ mDropDownPreference = new DropDownPreference(mContext);
+ mDropDownPreference.setEntries(R.array.wifi_metered_entries);
+ mDropDownPreference.setEntryValues(R.array.wifi_metered_values);
+ }
+
+ @Test
+ public void testUpdateState_wifiMetered_setCorrectValue() {
+ doReturn(METERED_OVERRIDE_METERED).when(mPreferenceController).getMeteredOverride();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.getEntry()).isEqualTo("Treat as metered");
+ }
+
+ @Test
+ public void testUpdateState_wifiNotMetered_setCorrectValue() {
+ doReturn(METERED_OVERRIDE_NOT_METERED).when(mPreferenceController).getMeteredOverride();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.getEntry()).isEqualTo("Treat as unmetered");
+ }
+
+ @Test
+ public void testUpdateState_wifiAuto_setCorrectValue() {
+ doReturn(METERED_OVERRIDE_NONE).when(mPreferenceController).getMeteredOverride();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.getEntry()).isEqualTo("Detect automatically");
+ }
+
+ @Test
+ public void testController_resilientToNullConfig() {
+ mPreferenceController = spy(new WifiMeteredPreferenceController2(mContext, null));
+
+ mPreferenceController.getMeteredOverride();
+ mPreferenceController.onPreferenceChange(mDropDownPreference, 1);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.java
new file mode 100644
index 0000000..91cc01e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+
+import androidx.preference.DropDownPreference;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiPrivacyPreferenceController2Test {
+
+ private static final int PRIVACY_RANDOMIZED = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ private static final int PRIVACY_TRUSTED = WifiConfiguration.RANDOMIZATION_NONE;
+
+ @Mock
+ private WifiConfiguration mWifiConfiguration;
+
+ private WifiPrivacyPreferenceController2 mPreferenceController;
+ private Context mContext;
+ private DropDownPreference mDropDownPreference;
+ private String[] mPerferenceStrings;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+
+ WifiPrivacyPreferenceController2 preferenceController =
+ new WifiPrivacyPreferenceController2(mContext);
+ preferenceController.setWifiConfiguration(mWifiConfiguration);
+ mPreferenceController = spy(preferenceController);
+ mDropDownPreference = new DropDownPreference(mContext);
+ mDropDownPreference.setEntries(R.array.wifi_privacy_entries);
+ mDropDownPreference.setEntryValues(R.array.wifi_privacy_values);
+
+ mPerferenceStrings = mContext.getResources().getStringArray(R.array.wifi_privacy_entries);
+ }
+
+ @Test
+ public void testUpdateState_wifiPrivacy_setCorrectValue() {
+ doReturn(PRIVACY_TRUSTED).when(mPreferenceController).getRandomizationValue();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ int prefValue = mPreferenceController.translateMacRandomizedValueToPrefValue(
+ PRIVACY_TRUSTED);
+ assertThat(mDropDownPreference.getEntry()).isEqualTo(mPerferenceStrings[prefValue]);
+ }
+
+ @Test
+ public void testUpdateState_wifiNotMetered_setCorrectValue() {
+ doReturn(PRIVACY_RANDOMIZED).when(mPreferenceController).getRandomizationValue();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ int prefValue = mPreferenceController.translateMacRandomizedValueToPrefValue(
+ PRIVACY_RANDOMIZED);
+ assertThat(mDropDownPreference.getEntry()).isEqualTo(mPerferenceStrings[prefValue]);
+ }
+
+ @Test
+ public void testController_resilientToNullConfig() {
+ mPreferenceController = spy(new WifiPrivacyPreferenceController2(mContext));
+
+ mPreferenceController.getRandomizationValue();
+ mPreferenceController.onPreferenceChange(mDropDownPreference, "1");
+ }
+
+ @Test
+ public void testUpdateState_isNotEphemeralNetwork_shouldBeSelectable() {
+ mPreferenceController.setIsEphemeral(false);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isTrue();
+ }
+
+ @Test
+ public void testUpdateState_isEphemeralNetwork_shouldNotSelectable() {
+ mPreferenceController.setIsEphemeral(true);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isFalse();
+ }
+
+ @Test
+ public void testUpdateState_isNotPasspointNetwork_shouldBeSelectable() {
+ mPreferenceController.setIsPasspoint(false);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isTrue();
+ }
+
+ @Test
+ public void testUpdateState_isPasspointNetwork_shouldNotSelectable() {
+ mPreferenceController.setIsPasspoint(true);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2Test.java
new file mode 100644
index 0000000..6be9852
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2Test.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowAccessPoint;
+import com.android.settings.testutils.shadow.ShadowWifiManager;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowWifiManager.class})
+public class SavedAccessPointsPreferenceController2Test {
+
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+
+ private Context mContext;
+ private WifiManager mWifiManager;
+ private SavedAccessPointsWifiSettings2 mSettings;
+ private SavedAccessPointsPreferenceController2 mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mWifiManager = mContext.getSystemService(WifiManager.class);
+ mSettings = spy(new SavedAccessPointsWifiSettings2());
+ mController = spy(new SavedAccessPointsPreferenceController2(mContext, "test_key"));
+ mController.setHost(mSettings);
+
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreferenceCategory);
+ when(mPreferenceCategory.getContext()).thenReturn(mContext);
+ }
+
+ @Test
+ public void getAvailability_noSavedAccessPoint_shouldNotAvailable() {
+ mController.mAccessPoints = new ArrayList<>();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailability_oneSavedAccessPoint_shouldAvailable() {
+ final AccessPoint accessPoint = new AccessPoint(mContext, new Bundle() /* savedState */);
+ mController.mAccessPoints = new ArrayList<AccessPoint>(Arrays.asList(accessPoint));
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ @Config(shadows = ShadowAccessPoint.class)
+ public void displayPreference_oneAccessPoint_shouldListNonSubscribedAPs() {
+ final WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "SSID";
+ config.BSSID = "BSSID";
+ config.networkId = 2;
+ mWifiManager.addNetwork(config);
+
+ final ArgumentCaptor<AccessPointPreference> captor =
+ ArgumentCaptor.forClass(AccessPointPreference.class);
+ mController.displayPreference(mPreferenceScreen);
+
+ verify(mPreferenceCategory).addPreference(captor.capture());
+
+ final AccessPointPreference pref = captor.getValue();
+ assertThat(pref.getTitle()).isEqualTo(config.SSID);
+ }
+
+ @Test
+ @Config(shadows = ShadowAccessPoint.class)
+ public void displayPreference_onePasspoint_shouldNotListSubscribedAPs() {
+ mWifiManager.addOrUpdatePasspointConfiguration(
+ SubscribedAccessPointsPreferenceController2Test.createMockPasspointConfiguration());
+
+ mController.displayPreference(mPreferenceScreen);
+
+ verify(mPreferenceCategory, never()).addPreference(any(AccessPointPreference.class));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
new file mode 100644
index 0000000..ab1b51c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class SavedAccessPointsWifiSettings2Test {
+
+ @Mock
+ private SubscribedAccessPointsPreferenceController2 mSubscribedApController;
+ @Mock
+ private SavedAccessPointsPreferenceController2 mSavedApController;
+
+ private TestFragment mSettings;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSettings = spy(new TestFragment());
+
+ doReturn(mSubscribedApController).when(mSettings)
+ .use(SubscribedAccessPointsPreferenceController2.class);
+ doReturn(mSavedApController).when(mSettings)
+ .use(SavedAccessPointsPreferenceController2.class);
+ }
+
+ @Test
+ public void verifyConstants() {
+ assertThat(mSettings.getMetricsCategory()).isEqualTo(MetricsEvent.WIFI_SAVED_ACCESS_POINTS);
+ assertThat(mSettings.getPreferenceScreenResId())
+ .isEqualTo(R.xml.wifi_display_saved_access_points2);
+ }
+
+ public static class TestFragment extends SavedAccessPointsWifiSettings2 {
+
+ public <T extends AbstractPreferenceController> T use(Class<T> clazz) {
+ return super.use(clazz);
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2Test.java
new file mode 100644
index 0000000..2ce5d44
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2Test.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.HomeSp;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowAccessPoint;
+import com.android.settings.testutils.shadow.ShadowWifiManager;
+import com.android.settingslib.wifi.AccessPointPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowWifiManager.class})
+public class SubscribedAccessPointsPreferenceController2Test {
+
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+
+ private Context mContext;
+ private WifiManager mWifiManager;
+ private SavedAccessPointsWifiSettings2 mSettings;
+ private SubscribedAccessPointsPreferenceController2 mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mWifiManager = mContext.getSystemService(WifiManager.class);
+ mSettings = spy(new SavedAccessPointsWifiSettings2());
+ mController = spy(new SubscribedAccessPointsPreferenceController2(mContext, "test_key"));
+ mController.setHost(mSettings);
+
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreferenceCategory);
+ when(mPreferenceCategory.getContext()).thenReturn(mContext);
+ }
+
+ @Test
+ @Config(shadows = ShadowAccessPoint.class)
+ public void displayPreference_oneAccessPoint_shouldNotListNonSubscribedAPs() {
+ final WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "SSID";
+ config.BSSID = "BSSID";
+ config.networkId = 2;
+ mWifiManager.addNetwork(config);
+
+ mController.displayPreference(mPreferenceScreen);
+
+ verify(mPreferenceCategory, never()).addPreference(any(AccessPointPreference.class));
+ }
+
+ @Test
+ @Config(shadows = ShadowAccessPoint.class)
+ public void displayPreference_onePasspoint_shouldListSubscribedAPs() {
+ mWifiManager.addOrUpdatePasspointConfiguration(createMockPasspointConfiguration());
+
+ mController.displayPreference(mPreferenceScreen);
+
+ final ArgumentCaptor<AccessPointPreference> captor =
+ ArgumentCaptor.forClass(AccessPointPreference.class);
+ verify(mPreferenceCategory).addPreference(captor.capture());
+
+ final AccessPointPreference pref = captor.getValue();
+ assertThat(pref.getTitle()).isEqualTo("TESTPASSPOINT");
+ }
+
+ public static PasspointConfiguration createMockPasspointConfiguration() {
+ final PasspointConfiguration config = new PasspointConfiguration();
+ final HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("FQDN");
+ homeSp.setFriendlyName("TESTPASSPOINT");
+ config.setHomeSp(homeSp);
+ return config;
+ }
+}