Show accurate connection status for ephemeral networks.

Configurations for these networks are not returned in
getConfiguredNetworks() and likely shouldn't be as clients of this API
would not expect them. (Note also that the ephemeral bit is marked
@hide). But the framework may connect to them regardless.

In these cases, as long as the connection status is something other
than the coarse-level DISCONNECTED, we show the status to be an
accurate representation of Wi-Fi state. (To make this possible, we
pass around the full NetworkInfo instead of just the DetailedState,
allowing us to get the coarse state where needed).

When long pressing on a non-DISCONNECTED ephemeral network, we offer
the ability to save the configuration. (Note that this flow is
currently broken and being tracked by another bug, but the behavior is
consistent with what happens when you simply click on the SSID).

Bug: 18205278
Change-Id: I30592c89546068c796f458a86bb26eb3b28c64df
This commit is contained in:
Jeff Davidson
2014-11-03 18:47:28 -08:00
parent 815c61d729
commit b46a9194fe
3 changed files with 92 additions and 39 deletions

View File

@@ -1505,6 +1505,8 @@
<string name="wifi_menu_advanced">Advanced</string>
<!-- Menu option to connect to a Wi-Fi network -->
<string name="wifi_menu_connect">Connect to network</string>
<!-- Menu option to remember a temporary Wi-Fi network -->
<string name="wifi_menu_remember">Remember network</string>
<!-- Menu option to delete a Wi-Fi network -->
<string name="wifi_menu_forget">Forget network</string>
<!-- Menu option to modify a Wi-Fi network configuration -->

View File

@@ -21,7 +21,9 @@ import com.android.settings.R;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -70,7 +72,7 @@ class AccessPoint extends Preference {
public LruCache<String, ScanResult> mScanResultCache;
private static final String KEY_DETAILEDSTATE = "key_detailedstate";
private static final String KEY_NETWORKINFO = "key_networkinfo";
private static final String KEY_WIFIINFO = "key_wifiinfo";
private static final String KEY_SCANRESULT = "key_scanresult";
private static final String KEY_CONFIG = "key_config";
@@ -113,7 +115,8 @@ class AccessPoint extends Preference {
private long mSeen = 0;
private WifiInfo mInfo;
private DetailedState mState;
private NetworkInfo mNetworkInfo;
private TextView mSummaryView;
private static final int VISIBILITY_MAX_AGE_IN_MILLI = 1000000;
private static final int VISIBILITY_OUTDATED_AGE_IN_MILLI = 20000;
@@ -211,18 +214,18 @@ class AccessPoint extends Preference {
loadResult(mScanResult);
}
mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO);
if (savedState.containsKey(KEY_DETAILEDSTATE)) {
mState = DetailedState.valueOf(savedState.getString(KEY_DETAILEDSTATE));
if (savedState.containsKey(KEY_NETWORKINFO)) {
mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO);
}
update(mInfo, mState);
update(mInfo, mNetworkInfo);
}
public void saveWifiState(Bundle savedState) {
savedState.putParcelable(KEY_CONFIG, mConfig);
savedState.putParcelable(KEY_SCANRESULT, mScanResult);
savedState.putParcelable(KEY_WIFIINFO, mInfo);
if (mState != null) {
savedState.putString(KEY_DETAILEDSTATE, mState.toString());
if (mNetworkInfo != null) {
savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
}
}
@@ -253,9 +256,8 @@ class AccessPoint extends Preference {
super.onBindView(view);
updateIcon(getLevel(), getContext());
final TextView summaryView = (TextView) view.findViewById(
com.android.internal.R.id.summary);
summaryView.setVisibility(showSummary ? View.VISIBLE : View.GONE);
mSummaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
mSummaryView.setVisibility(showSummary ? View.VISIBLE : View.GONE);
notifyChanged();
}
@@ -293,8 +295,8 @@ class AccessPoint extends Preference {
}
AccessPoint other = (AccessPoint) preference;
// Active one goes first.
if (mInfo != null && other.mInfo == null) return -1;
if (mInfo == null && other.mInfo != null) return 1;
if (isActive() && !other.isActive()) return -1;
if (!isActive() && other.isActive()) return 1;
// Reachable one goes before unreachable one.
if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1;
@@ -361,19 +363,30 @@ class AccessPoint extends Preference {
return false;
}
void update(WifiInfo info, DetailedState state) {
/** Return whether the given {@link WifiInfo} is for this access point. */
private boolean isInfoForThisAccessPoint(WifiInfo info) {
if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
return networkId == info.getNetworkId();
} else {
// Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
// (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
// TODO: Handle hex string SSIDs.
return ssid.equals(removeDoubleQuotes(info.getSSID()));
}
}
void update(WifiInfo info, NetworkInfo networkInfo) {
boolean reorder = false;
if (info != null && networkId != WifiConfiguration.INVALID_NETWORK_ID
&& networkId == info.getNetworkId()) {
if (info != null && isInfoForThisAccessPoint(info)) {
reorder = (mInfo == null);
mRssi = info.getRssi();
mInfo = info;
mState = state;
mNetworkInfo = networkInfo;
refresh();
} else if (mInfo != null) {
reorder = true;
mInfo = null;
mState = null;
mNetworkInfo = null;
refresh();
}
if (reorder) {
@@ -396,8 +409,12 @@ class AccessPoint extends Preference {
return mInfo;
}
NetworkInfo getNetworkInfo() {
return mNetworkInfo;
}
DetailedState getState() {
return mState;
return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null;
}
static String removeDoubleQuotes(String string) {
@@ -420,6 +437,9 @@ class AccessPoint extends Preference {
*/
public void setShowSummary(boolean showSummary) {
this.showSummary = showSummary;
if (mSummaryView != null) {
mSummaryView.setVisibility(showSummary ? View.VISIBLE : View.GONE);
} // otherwise, will be handled in onBindView.
}
/**
@@ -570,6 +590,17 @@ class AccessPoint extends Preference {
return visibility.toString();
}
/**
* Return whether this is the active connection.
* For ephemeral connections (networkId is invalid), this returns false if the network is
* disconnected.
*/
private boolean isActive() {
return mNetworkInfo != null &&
(networkId != WifiConfiguration.INVALID_NETWORK_ID ||
mNetworkInfo.getState() != State.DISCONNECTED);
}
/**
* Updates the title and summary; may indirectly call notifyChanged().
*/
@@ -585,8 +616,8 @@ class AccessPoint extends Preference {
// Update to new summary
StringBuilder summary = new StringBuilder();
if (mState != null) { // This is the active connection
summary.append(Summary.get(context, mState));
if (isActive()) {
summary.append(Summary.get(context, getState()));
} else if (mConfig != null && mConfig.noInternetAccess) {
summary.append(context.getString(R.string.wifi_no_internet));
} else if (mConfig != null && ((mConfig.status == WifiConfiguration.Status.DISABLED &&
@@ -628,7 +659,7 @@ class AccessPoint extends Preference {
if (WifiSettings.mVerboseLogging > 0) {
//add RSSI/band information for this config, what was seen up to 6 seconds ago
//verbose WiFi Logging is only turned on thru developers settings
if (mInfo != null && mState != null) { // This is the active connection
if (mInfo != null && mNetworkInfo != null) { // This is the active connection
summary.append(" f=" + Integer.toString(mInfo.getFrequency()));
}
summary.append(" " + getVisibilityStatus());
@@ -660,8 +691,9 @@ class AccessPoint extends Preference {
if (summary.length() > 0) {
setSummary(summary.toString());
setShowSummary(true);
} else {
showSummary = false;
setShowSummary(false);
}
}

View File

@@ -34,6 +34,7 @@ import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkScoreManager;
import android.net.NetworkScorerAppManager;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
@@ -132,7 +133,7 @@ public class WifiSettings extends RestrictedSettingsFragment
// An access point being editted is stored here.
private AccessPoint mSelectedAccessPoint;
private DetailedState mLastState;
private NetworkInfo mLastNetworkInfo;
private WifiInfo mLastInfo;
private final AtomicBoolean mConnected = new AtomicBoolean(false);
@@ -529,9 +530,22 @@ public class WifiSettings extends RestrictedSettingsFragment
if (preference instanceof AccessPoint) {
mSelectedAccessPoint = (AccessPoint) preference;
menu.setHeaderTitle(mSelectedAccessPoint.ssid);
if (mSelectedAccessPoint.getLevel() != -1
&& mSelectedAccessPoint.getState() == null) {
menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
if (mSelectedAccessPoint.getLevel() != -1) {
int connectStringRes = 0;
if (mSelectedAccessPoint.getState() == null) {
connectStringRes = R.string.wifi_menu_connect;
} else if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID &&
mSelectedAccessPoint.getNetworkInfo().getState()
!= State.DISCONNECTED) {
// State is non-null (and not disconnected) but this network has no
// configuration, which means it is ephemeral. Allow the user to save the
// configuration permanently (but still issue this as a CONNECT command).
connectStringRes = R.string.wifi_menu_remember;
}
if (connectStringRes != 0) {
menu.add(Menu.NONE, MENU_ID_CONNECT, 0, connectStringRes);
}
}
if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
if (ActivityManager.getCurrentUser() == UserHandle.USER_OWNER) {
@@ -671,7 +685,8 @@ public class WifiSettings extends RestrictedSettingsFragment
case WifiManager.WIFI_STATE_ENABLED:
// AccessPoints are automatically sorted with TreeSet.
final Collection<AccessPoint> accessPoints =
constructAccessPoints(getActivity(), mWifiManager, mLastInfo, mLastState);
constructAccessPoints(getActivity(), mWifiManager, mLastInfo,
mLastNetworkInfo);
getPreferenceScreen().removeAll();
if (accessPoints.size() == 0) {
addMessagePreference(R.string.wifi_empty_list_wifi_on);
@@ -833,7 +848,7 @@ public class WifiSettings extends RestrictedSettingsFragment
/** Returns sorted list of access points */
private static List<AccessPoint> constructAccessPoints(Context context,
WifiManager wifiManager, WifiInfo lastInfo, DetailedState lastState) {
WifiManager wifiManager, WifiInfo lastInfo, NetworkInfo lastNetworkInfo) {
ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
/** Lookup table to more quickly update AccessPoints by only considering objects with the
* correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */
@@ -853,8 +868,8 @@ public class WifiSettings extends RestrictedSettingsFragment
continue;
}
AccessPoint accessPoint = new AccessPoint(context, config);
if (lastInfo != null && lastState != null) {
accessPoint.update(lastInfo, lastState);
if (lastInfo != null && lastNetworkInfo != null) {
accessPoint.update(lastInfo, lastNetworkInfo);
}
accessPoints.add(accessPoint);
apMap.put(accessPoint.ssid, accessPoint);
@@ -877,6 +892,9 @@ public class WifiSettings extends RestrictedSettingsFragment
}
if (!found) {
AccessPoint accessPoint = new AccessPoint(context, result);
if (lastInfo != null && lastNetworkInfo != null) {
accessPoint.update(lastInfo, lastNetworkInfo);
}
accessPoints.add(accessPoint);
apMap.put(accessPoint.ssid, accessPoint);
}
@@ -903,28 +921,29 @@ public class WifiSettings extends RestrictedSettingsFragment
mConnected.set(info.isConnected());
changeNextButtonState(info.isConnected());
updateAccessPoints();
updateConnectionState(info.getDetailedState());
updateNetworkInfo(info);
} else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
updateConnectionState(null);
updateNetworkInfo(null);
}
}
private void updateConnectionState(DetailedState state) {
private void updateNetworkInfo(NetworkInfo networkInfo) {
/* sticky broadcasts can call this when wifi is disabled */
if (!mWifiManager.isWifiEnabled()) {
mScanner.pause();
return;
}
if (state == DetailedState.OBTAINING_IPADDR) {
if (networkInfo != null &&
networkInfo.getDetailedState() == DetailedState.OBTAINING_IPADDR) {
mScanner.pause();
} else {
mScanner.resume();
}
mLastInfo = mWifiManager.getConnectionInfo();
if (state != null) {
mLastState = state;
if (networkInfo != null) {
mLastNetworkInfo = networkInfo;
}
for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
@@ -932,7 +951,7 @@ public class WifiSettings extends RestrictedSettingsFragment
Preference preference = getPreferenceScreen().getPreference(i);
if (preference instanceof AccessPoint) {
final AccessPoint accessPoint = (AccessPoint) preference;
accessPoint.update(mLastInfo, mLastState);
accessPoint.update(mLastInfo, mLastNetworkInfo);
}
}
}
@@ -958,7 +977,7 @@ public class WifiSettings extends RestrictedSettingsFragment
}
mLastInfo = null;
mLastState = null;
mLastNetworkInfo = null;
mScanner.pause();
}