From 9051dfdd471a14e00cf42d6de1073c69bb48731f Mon Sep 17 00:00:00 2001 From: Quang Luong Date: Wed, 12 Dec 2018 12:49:24 -0800 Subject: [PATCH 1/3] Added initial OsuProvider support for AccessPoint and WifiTracker AccessPoint entries representing OSU providers should now appear in the wifi picker with their titles set to their OSU friendly name. Tapping on these entries will yield unexpected behavior for now, as the provisioning action has not been implemented yet. The RSSI has been set to an arbitrary value until these entries can be matched to their scan results. All available OSU providers will appear until a further CL removes entries for networks that have already been provisioned for. Tracking bug for adding robolectric tests: b/122849296 Test: manual, build and visual check Bug: 118705403 Change-Id: I30f61ae83100f80dab422ad742f23c32cdd2079a --- .../android/settingslib/wifi/AccessPoint.java | 98 ++++++++++++++----- .../android/settingslib/wifi/WifiTracker.java | 20 +++- 2 files changed, 91 insertions(+), 27 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index af5a24f16222d..a55bbc6d7d414 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -41,6 +41,7 @@ import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; +import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Bundle; import android.os.Parcelable; @@ -182,6 +183,10 @@ public class AccessPoint implements Comparable { public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE; + public static final String KEY_PREFIX_AP = "AP:"; + public static final String KEY_PREFIX_FQDN = "FQDN:"; + public static final String KEY_PREFIX_OSU = "OSU:"; + private final Context mContext; private String ssid; @@ -204,9 +209,6 @@ public class AccessPoint implements Comparable { @Speed private int mSpeed = Speed.NONE; private boolean mIsScoredNetworkMetered = false; - // used to co-relate internal vs returned accesspoint. - int mId; - /** * Information associated with the {@link PasspointConfiguration}. Only maintaining * the relevant info to preserve spaces. @@ -215,6 +217,8 @@ public class AccessPoint implements Comparable { private String mProviderFriendlyName; private boolean mIsCarrierAp = false; + + private OsuProvider mOsuProvider; /** * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP. */ @@ -280,14 +284,18 @@ public class AccessPoint implements Comparable { // Calculate required fields updateKey(); updateRssi(); - - mId = sLastId.incrementAndGet(); } + /** + * Creates an AccessPoint with only a WifiConfiguration. This is used for the saved networks + * page. + * + * Passpoint Credential AccessPoints should be created with this. + * Make sure to call setScanResults after constructing with this. + */ public AccessPoint(Context context, WifiConfiguration config) { mContext = context; loadConfig(config); - mId = sLastId.incrementAndGet(); } /** @@ -298,7 +306,19 @@ public class AccessPoint implements Comparable { mContext = context; mFqdn = config.getHomeSp().getFqdn(); mProviderFriendlyName = config.getHomeSp().getFriendlyName(); - mId = sLastId.incrementAndGet(); + } + + /** + * Initialize an AccessPoint object for a Passpoint OSU Provider. + * Make sure to call setScanResults after constructing with this. + */ + public AccessPoint(Context context, OsuProvider provider) { + mContext = context; + mOsuProvider = provider; + mRssi = 1; + // TODO: This placeholder SSID is here to avoid null pointer exceptions. + ssid = ""; + updateKey(); } AccessPoint(Context context, Collection results) { @@ -324,8 +344,6 @@ public class AccessPoint implements Comparable { mIsCarrierAp = firstResult.isCarrierAp; mCarrierApEapType = firstResult.carrierApEapType; mCarrierName = firstResult.carrierName; - - mId = sLastId.incrementAndGet(); } @VisibleForTesting void loadConfig(WifiConfiguration config) { @@ -344,14 +362,19 @@ public class AccessPoint implements Comparable { StringBuilder builder = new StringBuilder(); if (isPasspoint()) { - builder.append(mConfig.FQDN); - } else if (TextUtils.isEmpty(getSsidStr())) { - builder.append(getBssid()); - } else { - builder.append(getSsidStr()); + builder.append(KEY_PREFIX_FQDN).append(mConfig.FQDN); + } else if (isOsuProvider()) { + builder.append(KEY_PREFIX_OSU).append(mOsuProvider.getOsuSsid()); + builder.append(',').append(mOsuProvider.getServerUri()); + } else { // Non-Passpoint AP + builder.append(KEY_PREFIX_AP); + if (TextUtils.isEmpty(getSsidStr())) { + builder.append(getBssid()); + } else { + builder.append(getSsidStr()); + } + builder.append(',').append(getSecurity()); } - - builder.append(',').append(getSecurity()); mKey = builder.toString(); } @@ -396,8 +419,8 @@ public class AccessPoint implements Comparable { return difference; } - // Sort by ssid. - difference = getSsidStr().compareToIgnoreCase(other.getSsidStr()); + // Sort by title. + difference = getTitle().compareToIgnoreCase(other.getTitle()); if (difference != 0) { return difference; } @@ -595,6 +618,7 @@ public class AccessPoint implements Comparable { public static String getKey(ScanResult result) { StringBuilder builder = new StringBuilder(); + builder.append(KEY_PREFIX_AP); if (TextUtils.isEmpty(result.SSID)) { builder.append(result.BSSID); } else { @@ -609,14 +633,17 @@ public class AccessPoint implements Comparable { StringBuilder builder = new StringBuilder(); if (config.isPasspoint()) { - builder.append(config.FQDN); - } else if (TextUtils.isEmpty(config.SSID)) { - builder.append(config.BSSID); + builder.append(KEY_PREFIX_FQDN).append(config.FQDN); } else { - builder.append(removeDoubleQuotes(config.SSID)); + builder.append(KEY_PREFIX_AP); + if (TextUtils.isEmpty(config.SSID)) { + builder.append(config.BSSID); + } else { + builder.append(removeDoubleQuotes(config.SSID)); + } + builder.append(',').append(getSecurity(config)); } - builder.append(',').append(getSecurity(config)); return builder.toString(); } @@ -839,6 +866,8 @@ public class AccessPoint implements Comparable { public String getTitle() { if (isPasspoint()) { return mConfig.providerFriendlyName; + } else if (isOsuProvider()) { + return mOsuProvider.getFriendlyName(); } else { return getSsidStr(); } @@ -975,6 +1004,13 @@ public class AccessPoint implements Comparable { return mFqdn != null; } + /** + * Return true if this AccessPoint represents an OSU Provider. + */ + public boolean isOsuProvider() { + return mOsuProvider != null; + } + /** * Return whether the given {@link WifiInfo} is for this access point. * If the current AP does not have a network Id then the config is used to @@ -1065,8 +1101,8 @@ public class AccessPoint implements Comparable { void setScanResults(Collection scanResults) { // Validate scan results are for current AP only by matching SSID/BSSID - // Passpoint R1 networks are not bound to a specific SSID/BSSID, so skip this for passpoint. - if (!isPasspoint()) { + // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint. + if (!isPasspoint() && !isOsuProvider()) { String key = getKey(); for (ScanResult result : scanResults) { String scanResultKey = AccessPoint.getKey(result); @@ -1119,7 +1155,17 @@ public class AccessPoint implements Comparable { } } - /** Attempt to update the AccessPoint and return true if an update occurred. */ + /** + * Attempt to update the AccessPoint with the current connection info. + * This is used to set an AccessPoint to the active one if the connection info matches, or + * conversely to set an AccessPoint to inactive if the connection info does not match. The RSSI + * is also updated upon a match. Listeners will be notified if an update occurred. + * + * This is called in {@link WifiTracker#updateAccessPoints} as well as in callbacks for handling + * NETWORK_STATE_CHANGED_ACTION, RSSI_CHANGED_ACTION, and onCapabilitiesChanged in WifiTracker. + * + * Returns true if an update occurred. + */ public boolean update( @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 79a72402e232c..6d2889121c64f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -35,6 +35,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiNetworkScoreCache.CacheListener; +import android.net.wifi.hotspot2.OsuProvider; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -584,7 +585,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro Map>> pairing : passpointConfigsAndScans) { WifiConfiguration config = pairing.first; - // TODO: Prioritize home networks before roaming networks + // TODO(b/118705403): Prioritize home networks before roaming networks List scanResults = new ArrayList<>(); List homeScans = @@ -618,6 +619,23 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } + // Add Passpoint OSU Provider AccessPoints + // TODO(b/118705403): filter out OSU Providers which we already have credentials from. + Map> providersAndScans = + mWifiManager.getMatchingOsuProviders(cachedScanResults); + for (OsuProvider provider : providersAndScans.keySet()) { + AccessPoint accessPointOsu = new AccessPoint(mContext, provider); + // TODO(b/118705403): accessPointOsu.setScanResults(Matching ScanResult with best + // RSSI) + // TODO(b/118705403): Figure out if we would need to update an OSU AP (this will be + // used if we need to display it at the top of the picker as the "active" AP). + // Otherwise, OSU APs should ignore attempts to update the active connection + // info. + // accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo); + accessPoints.add(accessPointOsu); + } + + // If there were no scan results, create an AP for the currently connected network (if // it exists). if (accessPoints.isEmpty() && connectionConfig != null) { From 19bae88c82bdb5a5c1cdecdbcadd7771d2677b11 Mon Sep 17 00:00:00 2001 From: Quang Luong Date: Fri, 14 Dec 2018 16:05:05 -0800 Subject: [PATCH 2/3] Removed Passpoint Credential AP unconnected summary case The summary for Passpoint Credential APs should have the same behavior as regular APs while unconnected, displaying "Saved". Tracking bug for adding robolectric tests: b/122849296 Bug: 118705403 Test: manual, build and visual check Change-Id: I629319aa7d154af87fdcee43592c278fe7ddc51b --- .../android/settingslib/wifi/AccessPoint.java | 124 +++++++++--------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index a55bbc6d7d414..988541b90921a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -874,85 +874,87 @@ public class AccessPoint implements Comparable { } public String getSummary() { - return getSettingsSummary(mConfig); + return getSettingsSummary(); } public String getSettingsSummary() { - return getSettingsSummary(mConfig); - } - - private String getSettingsSummary(WifiConfiguration config) { // Update to new summary StringBuilder summary = new StringBuilder(); - if (isActive() && config != null && config.isPasspoint()) { - // This is the active connection on passpoint - summary.append(getSummary(mContext, getDetailedState(), - false, config.providerFriendlyName)); - } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED - && mIsCarrierAp) { - summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName)); - } else if (isActive()) { - // This is the active connection on non-passpoint network - summary.append(getSummary(mContext, getDetailedState(), - mInfo != null && mInfo.isEphemeral())); - } else if (config != null && config.isPasspoint() - && config.getNetworkSelectionStatus().isNetworkEnabled()) { - String format = mContext.getString(R.string.available_via_passpoint); - summary.append(String.format(format, config.providerFriendlyName)); - } else if (config != null && config.hasNoInternetAccess()) { - int messageID = config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() - ? R.string.wifi_no_internet_no_reconnect - : R.string.wifi_no_internet; - summary.append(mContext.getString(messageID)); - } else if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) { - WifiConfiguration.NetworkSelectionStatus networkStatus = - config.getNetworkSelectionStatus(); - switch (networkStatus.getNetworkSelectionDisableReason()) { - case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: - summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: - summary.append(mContext.getString(R.string.wifi_check_password_try_again)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: - case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE: - summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: - summary.append(mContext.getString(R.string.wifi_disabled_generic)); - break; + if (isActive()) { + if (isPasspoint()) { + // This is the active connection on passpoint + summary.append(getSummary(mContext, getDetailedState(), + false, mConfig.providerFriendlyName)); + } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED + && mIsCarrierAp) { + // This is the active connection on a carrier AP + summary.append(String.format(mContext.getString(R.string.connected_via_carrier), + mCarrierName)); + } else { + // This is the active connection on non-passpoint network + summary.append(getSummary(mContext, getDetailedState(), + mInfo != null && mInfo.isEphemeral())); } - } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) { - summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider)); - } else if (mIsCarrierAp) { - summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName)); - } else if (!isReachable()) { // Wifi out of range - summary.append(mContext.getString(R.string.wifi_not_in_range)); - } else { // In range, not disabled. - if (config != null) { // Is saved network - // Last attempt to connect to this failed. Show reason why - switch (config.recentFailure.getAssociationStatus()) { - case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: - summary.append(mContext.getString( - R.string.wifi_ap_unable_to_handle_new_sta)); + } else { // not active + if (mConfig != null && mConfig.hasNoInternetAccess()) { + int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() + ? R.string.wifi_no_internet_no_reconnect + : R.string.wifi_no_internet; + summary.append(mContext.getString(messageID)); + } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) { + WifiConfiguration.NetworkSelectionStatus networkStatus = + mConfig.getNetworkSelectionStatus(); + switch (networkStatus.getNetworkSelectionDisableReason()) { + case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: + summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); break; - default: - // "Saved" - summary.append(mContext.getString(R.string.wifi_remembered)); + case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: + summary.append(mContext.getString(R.string.wifi_check_password_try_again)); break; + case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: + case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE: + summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); + break; + case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: + summary.append(mContext.getString(R.string.wifi_disabled_generic)); + break; + } + } else if (mConfig != null && mConfig.getNetworkSelectionStatus().isNotRecommended()) { + summary.append(mContext.getString( + R.string.wifi_disabled_by_recommendation_provider)); + } else if (mIsCarrierAp) { + summary.append(String.format(mContext.getString( + R.string.available_via_carrier), mCarrierName)); + } else if (!isReachable()) { // Wifi out of range + summary.append(mContext.getString(R.string.wifi_not_in_range)); + } else { // In range, not disabled. + if (mConfig != null) { // Is saved network + // Last attempt to connect to this failed. Show reason why + switch (mConfig.recentFailure.getAssociationStatus()) { + case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: + summary.append(mContext.getString( + R.string.wifi_ap_unable_to_handle_new_sta)); + break; + default: + // "Saved" + summary.append(mContext.getString(R.string.wifi_remembered)); + break; + } } } } + + if (isVerboseLoggingEnabled()) { - summary.append(WifiUtils.buildLoggingSummary(this, config)); + summary.append(WifiUtils.buildLoggingSummary(this, mConfig)); } - if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) { + if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) { return mContext.getResources().getString( R.string.preference_summary_default_combination, - WifiUtils.getMeteredLabel(mContext, config), + WifiUtils.getMeteredLabel(mContext, mConfig), summary.toString()); } From 0a3edf0dc0fa1e6bfcc9a835ee42e2cf45239c4f Mon Sep 17 00:00:00 2001 From: Quang Luong Date: Mon, 17 Dec 2018 14:50:58 -0800 Subject: [PATCH 3/3] Display summary for connected Passpoint credential APs Connected Passpoint credential APs should now display by in their summary in the wifi picker. Tracking bug for adding robolectric tests: b/122849296 Bug: 118705403 Test: manual, build and visual check Change-Id: I5455dddae586379ffca9f7a840bef78942dc3ff1 --- packages/SettingsLib/res/values/strings.xml | 2 ++ .../src/com/android/settingslib/wifi/AccessPoint.java | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 03c620580423a..a29cf8449e51c 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -99,6 +99,8 @@ Automatically connected via network rating provider Connected via %1$s + + %1$s by %2$s Available via %1$s diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 988541b90921a..1ae1d56aacc81 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -884,7 +884,7 @@ public class AccessPoint implements Comparable { if (isActive()) { if (isPasspoint()) { // This is the active connection on passpoint - summary.append(getSummary(mContext, getDetailedState(), + summary.append(getSummary(mContext, ssid, getDetailedState(), false, mConfig.providerFriendlyName)); } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED && mIsCarrierAp) { @@ -1294,11 +1294,11 @@ public class AccessPoint implements Comparable { public static String getSummary(Context context, String ssid, DetailedState state, boolean isEphemeral, String passpointProvider) { - if (state == DetailedState.CONNECTED && ssid == null) { - if (TextUtils.isEmpty(passpointProvider) == false) { + if (state == DetailedState.CONNECTED) { + if (!TextUtils.isEmpty(passpointProvider)) { // Special case for connected + passpoint networks. - String format = context.getString(R.string.connected_via_passpoint); - return String.format(format, passpointProvider); + String format = context.getString(R.string.ssid_by_passpoint_provider); + return String.format(format, ssid, passpointProvider); } else if (isEphemeral) { // Special case for connected + ephemeral networks. final NetworkScoreManager networkScoreManager = context.getSystemService(