Files
packages_apps_Settings/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
lucaslin 7335c594be Modify dialog message for partial connectivity
In some networks, network validation may only get success
result for http probe but fail result for https probe.
For this kind of network, it may still work at some websites
or apps, but user didn't know about that. In order to fix this
issue, we will check if network has partial connectivity and
notify user to make a choice if they want to use this partial
connectivity or not.

Bug: 113450764
Bug: 128489091
Test: 1. Build pass.
      2. make -j44 RunSettingsRoboTests
      ROBOTEST_FILTER=WifiDetailPreferenceControllerTest
      3. Change captive_portal_https_url to https://invalid.com
      to simulate partial connectivity.

Change-Id: I0e87f6f2ede173f45a7b6fcf842b4f9a83d8efa1
2019-03-20 11:41:21 +08:00

834 lines
31 KiB
Java

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi.details;
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.drawable.Drawable;
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.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.fragment.app.Fragment;
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.development.featureflags.FeatureFlagPersistent;
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.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
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.util.StringJoiner;
import java.util.stream.Collectors;
/**
* Controller for logic pertaining to displaying Wifi information for the
* {@link WifiNetworkDetailsFragment}.
*/
public class WifiDetailPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, WifiDialogListener, LifecycleObserver, OnPause,
OnResume {
private static final String TAG = "WifiDetailsPrefCtrl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@VisibleForTesting
static final String KEY_HEADER = "connection_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_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 AccessPoint mAccessPoint;
private final ConnectivityManager mConnectivityManager;
private final Fragment 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;
// 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 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;
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:
updateNetworkInfo();
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;
updateIpLayerInfo();
}
}
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);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
// If the network just validated or lost Internet access or detected partial internet
// connectivity, refresh network state. Don't do this on every NetworkCapabilities
// change because refreshNetworkState sends IPCs to the system server from the UI
// thread, which can cause jank.
if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
if (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();
updateIpLayerInfo();
}
}
@Override
public void onLost(Network network) {
if (network.equals(mNetwork)) {
exitActivity();
}
}
};
private 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 + ")");
// Do nothing.
}
/** Called when the connection state of wifi has changed. */
public void onConnectedChanged() {
Log.d(TAG, "onConnectedChanged");
// Do nothing.
}
/**
* 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() {
Log.d(TAG, "onAccessPointsChanged");
updateNetworkInfo();
}
};
public static WifiDetailPreferenceController newInstance(
AccessPoint accessPoint,
ConnectivityManager connectivityManager,
Context context,
Fragment fragment,
Handler handler,
Lifecycle lifecycle,
WifiManager wifiManager,
MetricsFeatureProvider metricsFeatureProvider) {
return new WifiDetailPreferenceController(
accessPoint, connectivityManager, context, fragment, handler, lifecycle,
wifiManager, metricsFeatureProvider, new IconInjector(context));
}
@VisibleForTesting
/* package */ WifiDetailPreferenceController(
AccessPoint accessPoint,
ConnectivityManager connectivityManager,
Context context,
Fragment 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);
if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) {
mWifiTracker = WifiTrackerFactory.create(
mFragment.getActivity(),
mWifiListener,
mLifecycle,
true /*includeSaved*/,
true /*includeScans*/);
} else {
mWifiTracker = null;
}
}
@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)
.setButton2OnClickListener(view -> signIntoNetwork())
.setButton3Text(R.string.share)
.setButton3Icon(R.drawable.ic_qrcode_24dp)
.setButton3OnClickListener(view -> shareNetwork())
.setButton3Visible(WifiDppUtils.isSuportConfigurator(mContext, mAccessPoint));
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);
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("status_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.setBackground(
mContext.getDrawable(R.drawable.ic_settings_widget_background));
iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
mEntityHeaderController.setLabel(mAccessPoint.getTitle());
}
private void refreshEntityHeader() {
if (usingDataUsageHeader(mContext)) {
mSummaryHeaderController.updateState(mDataUsageSummaryPref);
} else {
mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
.done(mFragment.getActivity(), true /* rebind */);
}
}
@Override
public void onResume() {
// Ensure mNetwork is set before any callbacks above are delivered, since our
// NetworkCallback only looks at changes to mNetwork.
mNetwork = mWifiManager.getCurrentNetwork();
mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
updateNetworkInfo();
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 updateNetworkInfo() {
if(!updateAccessPoint()) {
return;
}
// 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
updateIpLayerInfo();
// MAC Address Pref
refreshMacAddress();
}
private boolean updateAccessPoint() {
boolean changed = false;
if (mWifiTracker != null) {
updateAccessPointFromScannedList();
// refresh UI if signal level changed for disconnect network.
changed = mRssiSignalLevel != mAccessPoint.getLevel();
}
if (mAccessPoint.isActive()) {
// No need to fetch LinkProperties and NetworkCapabilities, they are updated by the
// callbacks. mNetwork doesn't change except in onResume.
mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
mWifiInfo = mWifiManager.getConnectionInfo();
if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
exitActivity();
return false;
}
changed |= mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
// If feature for saved network not enabled, always return true.
return mWifiTracker == null || changed;
}
return changed;
}
private void updateAccessPointFromScannedList() {
mIsOutOfRange = true;
if (mAccessPoint.getConfig() == null) {
return;
}
for (AccessPoint ap : mWifiTracker.getAccessPoints()) {
if (ap.getConfig() != null
&& mAccessPoint.matches(ap.getConfig())) {
mAccessPoint = ap;
mIsOutOfRange = false;
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);
wifiIcon.setTintList(Utils.getColorAccent(mContext));
if (mEntityHeaderController != null) {
mEntityHeaderController.setIcon(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 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);
}
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 refreshMacAddress() {
String macAddress = getMacAddress();
if (macAddress == null) {
mMacAddressPref.setVisible(false);
return;
}
mMacAddressPref.setVisible(true);
mMacAddressPref.setSummary(macAddress);
}
private String getMacAddress() {
if (mWifiInfo != null) {
// get MAC address from connected network information
return mWifiInfo.getMacAddress();
}
// return randomized MAC address
if (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() {
mButtonsPref.setButton1Visible(canForgetNetwork());
mButtonsPref.setButton2Visible(canSignIntoNetwork());
mButtonsPref.setButton3Visible(canShareNetwork());
mButtonsPref.setVisible(
canSignIntoNetwork() || canForgetNetwork() || canShareNetwork());
}
private void updateIpLayerInfo() {
// 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();
}
/**
* 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 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;
}
/**
* Forgets the wifi network associated with this preference.
*/
private void forgetNetwork() {
if (mWifiInfo != null && mWifiInfo.isEphemeral()) {
mWifiManager.disableEphemeralNetwork(mWifiInfo.getSSID());
} else if (mWifiConfig != null) {
if (mWifiConfig.isPasspoint()) {
// Post a dialog to confirm if user really want to forget the passpoint network.
if (FeatureFlagPersistent.isEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2)) {
showConfirmForgetDialog();
return;
}
mWifiManager.removePasspointConfiguration(mWifiConfig.FQDN);
} else {
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) -> {
mWifiManager.removePasspointConfiguration(mWifiConfig.FQDN);
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.
*/
public void launchWifiDppConfiguratorActivity() {
final Intent intent = WifiDppUtils.getConfiguratorIntentOrNull(mContext, mWifiManager,
mAccessPoint);
if (intent == null) {
Log.e(TAG, "Launch Wi-Fi DPP configurator with a wrong Wi-Fi network!");
} else {
mContext.startActivity(intent);
}
}
/**
* Share the wifi network with QR code.
*/
private void shareNetwork() {
final String title = mContext.getString(
R.string.lockpassword_confirm_your_pattern_header);
final String description = String.format(
mContext.getString(R.string.wifi_sharing_message),
mAccessPoint.getSsidStr());
WifiDppUtils.showLockScreen(mContext, title, description,
() -> 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;
public 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);
}
}